From 7d42784963608667ec6f1819b38c0fd7beb00abf Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 23 Dec 2022 14:09:47 +0100 Subject: [PATCH 001/799] Add registry settings --- app/main.f90 | 10 ++++++++++ src/fpm_command_line.f90 | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/main.f90 b/app/main.f90 index c7091267fb..befab9cba7 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -2,6 +2,7 @@ program main use, intrinsic :: iso_fortran_env, only : error_unit, output_unit use fpm_command_line, only: & fpm_cmd_settings, & + fpm_registry_settings, & fpm_new_settings, & fpm_build_settings, & fpm_run_settings, & @@ -21,11 +22,14 @@ program main implicit none class(fpm_cmd_settings), allocatable :: cmd_settings +type(fpm_registry_settings), allocatable :: registry_settings type(error_t), allocatable :: error character(len=:), allocatable :: pwd_start, pwd_working, working_dir, project_root call get_command_line_settings(cmd_settings) +call get_registry(registry_settings) + call get_current_directory(pwd_start, error) call handle_error(error) @@ -106,6 +110,12 @@ subroutine handle_error(error) stop 1 end if end subroutine handle_error + + !> Obtain registry settings and register local or custom registry + !> if such was specified in the global config file. + subroutine get_registry(reg_settings) + type(fpm_registry_settings), allocatable, intent(out) :: reg_settings + end subroutine get_registry !> Save access to working directory in settings, in case setting have not been allocated subroutine get_working_dir(settings, working_dir) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 659acd1950..b2d1b3321a 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -41,6 +41,7 @@ module fpm_command_line private public :: fpm_cmd_settings, & + fpm_registry_settings, & fpm_build_settings, & fpm_install_settings, & fpm_new_settings, & @@ -52,10 +53,19 @@ module fpm_command_line type, abstract :: fpm_cmd_settings character(len=:), allocatable :: working_dir - logical :: verbose=.true. + logical :: verbose=.true. end type integer,parameter :: ibug=4096 + +type, extends(fpm_cmd_settings) :: fpm_registry_settings + character(len=:), allocatable :: path + character(len=:), allocatable :: url + +contains + procedure :: uses_default_registry +end type + type, extends(fpm_cmd_settings) :: fpm_new_settings character(len=:),allocatable :: name logical :: with_executable=.false. @@ -1322,4 +1332,10 @@ function get_fpm_env(env, default) result(val) val = get_env(fpm_prefix//env, default) end function get_fpm_env + !> The official registry is used by default when no local or custom registry was specified. + pure logical function uses_default_registry(self) + class(fpm_registry_settings), intent(in) :: self + uses_default_registry = .not. allocated(self%path) .and. .not. allocated(self%url) + end function + end module fpm_command_line From 8787dd5faa66880b3ee6a8dc133df9981f7885d9 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 25 Dec 2022 16:27:04 +0100 Subject: [PATCH 002/799] Rename set_default_prefix and move to fpm_filesystem --- src/fpm/installer.f90 | 42 +++--------------------------------------- src/fpm_filesystem.F90 | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/fpm/installer.f90 b/src/fpm/installer.f90 index 4e138d10e3..e250b196ae 100644 --- a/src/fpm/installer.f90 +++ b/src/fpm/installer.f90 @@ -7,14 +7,12 @@ module fpm_installer use, intrinsic :: iso_fortran_env, only : output_unit use fpm_environment, only : get_os_type, os_is_unix use fpm_error, only : error_t, fatal_error - use fpm_filesystem, only : join_path, mkdir, exists, unix_path, windows_path, & - env_variable + use fpm_filesystem, only : join_path, mkdir, exists, unix_path, windows_path, set_local_prefix + implicit none private - public :: installer_t, new_installer - !> Declaration of the installer type type :: installer_t !> Path to installation directory @@ -59,12 +57,6 @@ module fpm_installer !> Default name of the include subdirectory character(len=*), parameter :: default_includedir = "include" - !> Default name of the installation prefix on Unix platforms - character(len=*), parameter :: default_prefix_unix = "/usr/local" - - !> Default name of the installation prefix on Windows platforms - character(len=*), parameter :: default_prefix_win = "C:\" - !> Copy command on Unix platforms character(len=*), parameter :: default_copy_unix = "cp" @@ -77,7 +69,6 @@ module fpm_installer !> Move command on Windows platforms character(len=*), parameter :: default_move_win = "move" - contains !> Create a new instance of an installer @@ -131,7 +122,7 @@ subroutine new_installer(self, prefix, bindir, libdir, includedir, verbosity, & if (present(prefix)) then self%prefix = prefix else - call set_default_prefix(self%prefix, self%os) + call set_local_prefix(self%prefix, self%os) end if if (present(bindir)) then @@ -154,33 +145,6 @@ subroutine new_installer(self, prefix, bindir, libdir, includedir, verbosity, & end subroutine new_installer - !> Set the default prefix for the installation - subroutine set_default_prefix(prefix, os) - !> Installation prefix - character(len=:), allocatable :: prefix - !> Platform identifier - integer, intent(in), optional :: os - - character(len=:), allocatable :: home - - if (os_is_unix(os)) then - call env_variable(home, "HOME") - if (allocated(home)) then - prefix = join_path(home, ".local") - else - prefix = default_prefix_unix - end if - else - call env_variable(home, "APPDATA") - if (allocated(home)) then - prefix = join_path(home, "local") - else - prefix = default_prefix_win - end if - end if - - end subroutine set_default_prefix - !> Install an executable in its correct subdirectory subroutine install_executable(self, executable, error) !> Instance of the installer diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index e60b2df1f7..9618113051 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -11,8 +11,8 @@ module fpm_filesystem use fpm_error, only : fpm_stop implicit none private - public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, env_variable, & - mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file + public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, & + mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, set_local_prefix public :: fileopen, fileclose, filewrite, warnwrite, parent_dir public :: is_hidden_file public :: read_lines, read_lines_expanded @@ -963,4 +963,36 @@ subroutine os_delete_dir(unix, dir, echo) end subroutine os_delete_dir +!> Set path prefix to the local folder. Used for installation, registry etc. +subroutine set_local_prefix(prefix, os) + !> Installation prefix + character(len=:), allocatable :: prefix + !> Platform identifier + integer, intent(in), optional :: os + + !> Default installation prefix on Unix platforms + character(len=*), parameter :: default_prefix_unix = "/usr/local" + !> Default installation prefix on Windows platforms + character(len=*), parameter :: default_prefix_win = "C:\" + + character(len=:), allocatable :: home + + if (os_is_unix(os)) then + call env_variable(home, "HOME") + if (allocated(home)) then + prefix = join_path(home, ".local") + else + prefix = default_prefix_unix + end if + else + call env_variable(home, "APPDATA") + if (allocated(home)) then + prefix = join_path(home, "local") + else + prefix = default_prefix_win + end if + end if + + end subroutine set_local_prefix + end module fpm_filesystem From baff313f99a66d36af5b63c65c1753335c827e77 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 25 Dec 2022 16:56:08 +0100 Subject: [PATCH 003/799] Use function instead of subroutine --- src/fpm/installer.f90 | 4 ++-- src/fpm_filesystem.F90 | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fpm/installer.f90 b/src/fpm/installer.f90 index e250b196ae..ec5880fe51 100644 --- a/src/fpm/installer.f90 +++ b/src/fpm/installer.f90 @@ -7,7 +7,7 @@ module fpm_installer use, intrinsic :: iso_fortran_env, only : output_unit use fpm_environment, only : get_os_type, os_is_unix use fpm_error, only : error_t, fatal_error - use fpm_filesystem, only : join_path, mkdir, exists, unix_path, windows_path, set_local_prefix + use fpm_filesystem, only : join_path, mkdir, exists, unix_path, windows_path, get_local_prefix implicit none private @@ -122,7 +122,7 @@ subroutine new_installer(self, prefix, bindir, libdir, includedir, verbosity, & if (present(prefix)) then self%prefix = prefix else - call set_local_prefix(self%prefix, self%os) + self%prefix = get_local_prefix(self%os) end if if (present(bindir)) then diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 9618113051..4e542fc2fe 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -11,8 +11,8 @@ module fpm_filesystem use fpm_error, only : fpm_stop implicit none private - public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, & - mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, set_local_prefix + public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & + mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file public :: fileopen, fileclose, filewrite, warnwrite, parent_dir public :: is_hidden_file public :: read_lines, read_lines_expanded @@ -964,7 +964,7 @@ subroutine os_delete_dir(unix, dir, echo) end subroutine os_delete_dir !> Set path prefix to the local folder. Used for installation, registry etc. -subroutine set_local_prefix(prefix, os) +function get_local_prefix(os) result(prefix) !> Installation prefix character(len=:), allocatable :: prefix !> Platform identifier @@ -993,6 +993,6 @@ subroutine set_local_prefix(prefix, os) end if end if - end subroutine set_local_prefix +end function get_local_prefix end module fpm_filesystem From 15de7fe2e31cf4deed91a4b488a5ea11bb60c42d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 25 Dec 2022 22:29:23 +0100 Subject: [PATCH 004/799] Extract get_registry into its own fpm_registry module --- app/main.f90 | 7 +------ src/fpm_registry.f90 | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 src/fpm_registry.f90 diff --git a/app/main.f90 b/app/main.f90 index befab9cba7..6e7e0226ac 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -18,6 +18,7 @@ program main use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update use fpm_os, only: change_directory, get_current_directory +use fpm_registry, only: get_registry implicit none @@ -110,12 +111,6 @@ subroutine handle_error(error) stop 1 end if end subroutine handle_error - - !> Obtain registry settings and register local or custom registry - !> if such was specified in the global config file. - subroutine get_registry(reg_settings) - type(fpm_registry_settings), allocatable, intent(out) :: reg_settings - end subroutine get_registry !> Save access to working directory in settings, in case setting have not been allocated subroutine get_working_dir(settings, working_dir) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 new file mode 100644 index 0000000000..d10705e581 --- /dev/null +++ b/src/fpm_registry.f90 @@ -0,0 +1,20 @@ +module fpm_registry + use fpm_command_line, only: fpm_registry_settings + use fpm_filesystem, only: exists, join_path, get_local_prefix + implicit none + private + public get_registry + +contains + !> Obtain registry settings and register local or custom registry if such was specified + !> in the global config file. + subroutine get_registry(reg_settings) + type(fpm_registry_settings), allocatable, intent(out) :: reg_settings + character(len=:), allocatable :: path_to_config_file + path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') + if (exists(path_to_config_file)) then + reg_settings%working_dir = path_to_config_file + end if + end subroutine get_registry + +end module fpm_registry From f7cf187fb858d3454f82ed4f87304065b28cbc06 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 25 Dec 2022 23:13:46 +0100 Subject: [PATCH 005/799] Add test for no config file --- test/fpm_test/main.f90 | 4 +++- test/fpm_test/test_registry.f90 | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/fpm_test/test_registry.f90 diff --git a/test/fpm_test/main.f90 b/test/fpm_test/main.f90 index 0a653076d6..d47f0ce70c 100644 --- a/test/fpm_test/main.f90 +++ b/test/fpm_test/main.f90 @@ -12,6 +12,7 @@ program fpm_testing use test_backend, only: collect_backend use test_installer, only : collect_installer use test_versioning, only : collect_versioning + use test_registry, only : collect_registry implicit none integer :: stat, is character(len=:), allocatable :: suite_name, test_name @@ -29,7 +30,8 @@ program fpm_testing & new_testsuite("fpm_package_dependencies", collect_package_dependencies), & & new_testsuite("fpm_test_backend", collect_backend), & & new_testsuite("fpm_installer", collect_installer), & - & new_testsuite("fpm_versioning", collect_versioning) & + & new_testsuite("fpm_versioning", collect_versioning), & + & new_testsuite("fpm_registry", collect_registry) & & ] call get_argument(1, suite_name) diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 new file mode 100644 index 0000000000..45b7a8e7de --- /dev/null +++ b/test/fpm_test/test_registry.f90 @@ -0,0 +1,37 @@ +module test_registry + use testsuite, only: new_unittest, unittest_t, error_t, test_failed + use fpm_command_line, only: fpm_registry_settings + use fpm_registry, only: get_registry + implicit none + private + public collect_registry + +contains + + !> Collect unit tests. + subroutine collect_registry(tests) + + !> Unit tests to collect. + type(unittest_t), allocatable, intent(out) :: tests(:) + + tests = [ & + & new_unittest('no-file', no_file) & + ] + + end subroutine collect_registry + + subroutine no_file(error) + + type(error_t), allocatable, intent(out) :: error + + type(fpm_registry_settings), allocatable :: registry_settings + + call get_registry(registry_settings) + + if (allocated(registry_settings)) then + call test_failed(error, 'registry_settings should not be allocated without config file') + end if + + end subroutine no_file + +end module test_registry From 8d815c4d7e8784f9b564acc2ddfac6fd7cb90df5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 00:53:37 +0100 Subject: [PATCH 006/799] Inject path for reproducibility --- src/fpm_registry.f90 | 21 ++++++++++++++++----- test/fpm_test/test_registry.f90 | 8 +++++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index d10705e581..4717156fc0 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -8,12 +8,23 @@ module fpm_registry contains !> Obtain registry settings and register local or custom registry if such was specified !> in the global config file. - subroutine get_registry(reg_settings) + subroutine get_registry(reg_settings, path_to_config_file) + !> Registry settings to be obtained. type(fpm_registry_settings), allocatable, intent(out) :: reg_settings - character(len=:), allocatable :: path_to_config_file - path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') - if (exists(path_to_config_file)) then - reg_settings%working_dir = path_to_config_file + !> Custom path to the config file. + character(len=:), allocatable, optional, intent(in) :: path_to_config_file + !> System-dependent default path to the config file. + character(len=:), allocatable :: default_path_to_config_file + + if (present(path_to_config_file)) then + if (exists(path_to_config_file)) then + reg_settings%working_dir = path_to_config_file + end if + end if + + default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') + if (exists(default_path_to_config_file)) then + reg_settings%working_dir = default_path_to_config_file end if end subroutine get_registry diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index 45b7a8e7de..dfa3793e36 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -23,13 +23,15 @@ end subroutine collect_registry subroutine no_file(error) type(error_t), allocatable, intent(out) :: error - type(fpm_registry_settings), allocatable :: registry_settings + character(len=:), allocatable :: dummy_path + + dummy_path = 'non_existent_path_to/config.toml' - call get_registry(registry_settings) + call get_registry(registry_settings, dummy_path) if (allocated(registry_settings)) then - call test_failed(error, 'registry_settings should not be allocated without config file') + call test_failed(error, 'registry_settings should not be allocated without a config file') end if end subroutine no_file From db27755909b5e66f6bbefcb6e8b9bd3b9180c394 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 14:09:18 +0100 Subject: [PATCH 007/799] Improve test --- src/fpm_registry.f90 | 2 +- test/fpm_test/test_registry.f90 | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 4717156fc0..1c7abe34f6 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -12,7 +12,7 @@ subroutine get_registry(reg_settings, path_to_config_file) !> Registry settings to be obtained. type(fpm_registry_settings), allocatable, intent(out) :: reg_settings !> Custom path to the config file. - character(len=:), allocatable, optional, intent(in) :: path_to_config_file + character(len=*), optional, intent(in) :: path_to_config_file !> System-dependent default path to the config file. character(len=:), allocatable :: default_path_to_config_file diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index dfa3793e36..f9b09239dc 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -2,6 +2,8 @@ module test_registry use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_command_line, only: fpm_registry_settings use fpm_registry, only: get_registry + use fpm_filesystem, only: exists, join_path + implicit none private public collect_registry @@ -24,11 +26,13 @@ subroutine no_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_registry_settings), allocatable :: registry_settings - character(len=:), allocatable :: dummy_path + character(len=*), parameter :: dummy_folder = 'dummy_folder' - dummy_path = 'non_existent_path_to/config.toml' + if (exists(dummy_folder)) then + call test_failed(error, 'dummy_folder should not exist before test') + end if - call get_registry(registry_settings, dummy_path) + call get_registry(registry_settings, join_path(dummy_folder, 'config.toml')) if (allocated(registry_settings)) then call test_failed(error, 'registry_settings should not be allocated without a config file') From 1bb98d500f72515b4b4aeb66154d6b2c46ec50cc Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 14:14:13 +0100 Subject: [PATCH 008/799] Use correct function --- test/fpm_test/test_registry.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index f9b09239dc..a7486e8712 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -2,7 +2,7 @@ module test_registry use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_command_line, only: fpm_registry_settings use fpm_registry, only: get_registry - use fpm_filesystem, only: exists, join_path + use fpm_filesystem, only: is_dir, join_path implicit none private @@ -28,7 +28,7 @@ subroutine no_file(error) type(fpm_registry_settings), allocatable :: registry_settings character(len=*), parameter :: dummy_folder = 'dummy_folder' - if (exists(dummy_folder)) then + if (is_dir(dummy_folder)) then call test_failed(error, 'dummy_folder should not exist before test') end if From 24ccb14600fc14de772c1897b1220fe7ed4befa1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 14:20:13 +0100 Subject: [PATCH 009/799] Use concatination --- test/fpm_test/test_registry.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index a7486e8712..2ffe9c51f6 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -29,7 +29,7 @@ subroutine no_file(error) character(len=*), parameter :: dummy_folder = 'dummy_folder' if (is_dir(dummy_folder)) then - call test_failed(error, 'dummy_folder should not exist before test') + call test_failed(error, dummy_folder//' should not exist before test') end if call get_registry(registry_settings, join_path(dummy_folder, 'config.toml')) From 44a410ea0c19b73541a7cff507ab010a0a78cd23 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 18:08:58 +0100 Subject: [PATCH 010/799] Add test for empty file and fix bugs --- src/fpm_registry.f90 | 11 +++++--- test/fpm_test/test_registry.f90 | 45 ++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 1c7abe34f6..290a3659c8 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -18,14 +18,17 @@ subroutine get_registry(reg_settings, path_to_config_file) if (present(path_to_config_file)) then if (exists(path_to_config_file)) then + allocate (reg_settings) reg_settings%working_dir = path_to_config_file end if + else + default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') + if (exists(default_path_to_config_file)) then + allocate (reg_settings) + reg_settings%working_dir = default_path_to_config_file + end if end if - default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') - if (exists(default_path_to_config_file)) then - reg_settings%working_dir = default_path_to_config_file - end if end subroutine get_registry end module fpm_registry diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index 2ffe9c51f6..a4f43e813e 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -2,12 +2,16 @@ module test_registry use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_command_line, only: fpm_registry_settings use fpm_registry, only: get_registry - use fpm_filesystem, only: is_dir, join_path + use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir + use fpm_environment, only: os_is_unix implicit none private public collect_registry + character(len=*), parameter :: tmp_folder = 'tmp' + character(len=*), parameter :: config_file_name = 'config.toml' + contains !> Collect unit tests. @@ -17,7 +21,8 @@ subroutine collect_registry(tests) type(unittest_t), allocatable, intent(out) :: tests(:) tests = [ & - & new_unittest('no-file', no_file) & + & new_unittest('no-file', no_file), & + & new_unittest('empty-file', empty_file) & ] end subroutine collect_registry @@ -26,13 +31,12 @@ subroutine no_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_registry_settings), allocatable :: registry_settings - character(len=*), parameter :: dummy_folder = 'dummy_folder' - if (is_dir(dummy_folder)) then - call test_failed(error, dummy_folder//' should not exist before test') + if (is_dir(tmp_folder)) then + call test_failed(error, 'Folder "'//tmp_folder//'" should not exist before test') end if - call get_registry(registry_settings, join_path(dummy_folder, 'config.toml')) + call get_registry(registry_settings, join_path(tmp_folder, config_file_name)) if (allocated(registry_settings)) then call test_failed(error, 'registry_settings should not be allocated without a config file') @@ -40,4 +44,33 @@ subroutine no_file(error) end subroutine no_file + subroutine empty_file(error) + + type(error_t), allocatable, intent(out) :: error + type(fpm_registry_settings), allocatable :: registry_settings + character(len=:), allocatable :: path_to_config_file + + if (is_dir(tmp_folder)) then + call test_failed(error, 'Folder "'//tmp_folder//'" should not exist before test') + end if + + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['']) + + call get_registry(registry_settings, path_to_config_file) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (.not. allocated(registry_settings)) then + call test_failed(error, 'registry_settings not allocated') + end if + + if (.not. allocated(registry_settings%working_dir)) then + call test_failed(error, 'registry_settings%working_dir not allocated') + end if + + end subroutine empty_file + end module test_registry From 4684acfaf03be1a58bac1f62031cc06a43134333 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 18:32:26 +0100 Subject: [PATCH 011/799] Unify logic --- src/fpm_registry.f90 | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 290a3659c8..6fbf9a2b68 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -8,27 +8,35 @@ module fpm_registry contains !> Obtain registry settings and register local or custom registry if such was specified !> in the global config file. - subroutine get_registry(reg_settings, path_to_config_file) + subroutine get_registry(reg_settings, custom_path_to_config_file) !> Registry settings to be obtained. type(fpm_registry_settings), allocatable, intent(out) :: reg_settings !> Custom path to the config file. - character(len=*), optional, intent(in) :: path_to_config_file + character(len=*), optional, intent(in) :: custom_path_to_config_file !> System-dependent default path to the config file. character(len=:), allocatable :: default_path_to_config_file + !> Final path to the config file. + character(len=:), allocatable :: path_to_config_file - if (present(path_to_config_file)) then - if (exists(path_to_config_file)) then - allocate (reg_settings) - reg_settings%working_dir = path_to_config_file + ! Use custom path to the config file if it was specified. + if (present(custom_path_to_config_file)) then + if (exists(custom_path_to_config_file)) then + path_to_config_file = custom_path_to_config_file end if else + ! Use default path to the config file if it wasn't manually set and exists. default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') if (exists(default_path_to_config_file)) then - allocate (reg_settings) - reg_settings%working_dir = default_path_to_config_file + path_to_config_file = default_path_to_config_file end if end if + ! Obtain registry settings from config file if it was found. + if (allocated(path_to_config_file)) then + allocate (reg_settings) + reg_settings%working_dir = path_to_config_file + end if + end subroutine get_registry end module fpm_registry From 3c1aff66a179b924cc005ceb60e8167fb4edf115 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 19:54:48 +0100 Subject: [PATCH 012/799] Add error handling if custom path could not get resolved, add docs, fix tests by adding returns --- app/main.f90 | 3 ++- src/fpm_registry.f90 | 7 ++++++- test/fpm_test/test_registry.f90 | 31 ++++++++++++++++++------------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index 6e7e0226ac..26ade218b9 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -29,7 +29,8 @@ program main call get_command_line_settings(cmd_settings) -call get_registry(registry_settings) +call get_registry(registry_settings, error) +call handle_error(error) call get_current_directory(pwd_start, error) call handle_error(error) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 6fbf9a2b68..50dfee46a1 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -1,6 +1,7 @@ module fpm_registry use fpm_command_line, only: fpm_registry_settings use fpm_filesystem, only: exists, join_path, get_local_prefix + use fpm_error, only: error_t, fatal_error implicit none private public get_registry @@ -8,9 +9,11 @@ module fpm_registry contains !> Obtain registry settings and register local or custom registry if such was specified !> in the global config file. - subroutine get_registry(reg_settings, custom_path_to_config_file) + subroutine get_registry(reg_settings, error, custom_path_to_config_file) !> Registry settings to be obtained. type(fpm_registry_settings), allocatable, intent(out) :: reg_settings + !> Error handling. + type(error_t), allocatable, intent(out) :: error !> Custom path to the config file. character(len=*), optional, intent(in) :: custom_path_to_config_file !> System-dependent default path to the config file. @@ -22,6 +25,8 @@ subroutine get_registry(reg_settings, custom_path_to_config_file) if (present(custom_path_to_config_file)) then if (exists(custom_path_to_config_file)) then path_to_config_file = custom_path_to_config_file + else + call fatal_error(error, 'No config.toml at: "'//custom_path_to_config_file//'"') end if else ! Use default path to the config file if it wasn't manually set and exists. diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index a4f43e813e..25b50818bf 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -2,7 +2,7 @@ module test_registry use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_command_line, only: fpm_registry_settings use fpm_registry, only: get_registry - use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir + use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix implicit none @@ -21,54 +21,59 @@ subroutine collect_registry(tests) type(unittest_t), allocatable, intent(out) :: tests(:) tests = [ & - & new_unittest('no-file', no_file), & + & new_unittest('no-tmp-folder', no_tmp_folder), & + & new_unittest('no-file', no_file, should_fail=.true.), & & new_unittest('empty-file', empty_file) & ] end subroutine collect_registry - subroutine no_file(error) + !> Makes sure no `tmp` folder exists, important for other tests. + subroutine no_tmp_folder(error) type(error_t), allocatable, intent(out) :: error - type(fpm_registry_settings), allocatable :: registry_settings if (is_dir(tmp_folder)) then call test_failed(error, 'Folder "'//tmp_folder//'" should not exist before test') + return end if - call get_registry(registry_settings, join_path(tmp_folder, config_file_name)) + end subroutine no_tmp_folder - if (allocated(registry_settings)) then - call test_failed(error, 'registry_settings should not be allocated without a config file') - end if + !> Throw error when custom path to config file was entered but none exists. + subroutine no_file(error) + + type(error_t), allocatable, intent(out) :: error + type(fpm_registry_settings), allocatable :: registry_settings + + call get_registry(registry_settings, error, join_path(tmp_folder, config_file_name)) end subroutine no_file + !> Config file exists and working directory is set. subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_registry_settings), allocatable :: registry_settings character(len=:), allocatable :: path_to_config_file - if (is_dir(tmp_folder)) then - call test_failed(error, 'Folder "'//tmp_folder//'" should not exist before test') - end if - call mkdir(tmp_folder) path_to_config_file = join_path(tmp_folder, config_file_name) call filewrite(path_to_config_file, ['']) - call get_registry(registry_settings, path_to_config_file) + call get_registry(registry_settings, error, path_to_config_file) call os_delete_dir(os_is_unix(), tmp_folder) if (.not. allocated(registry_settings)) then call test_failed(error, 'registry_settings not allocated') + return end if if (.not. allocated(registry_settings%working_dir)) then call test_failed(error, 'registry_settings%working_dir not allocated') + return end if end subroutine empty_file From ff7429a784549db2c77a696f42db5fc91960f65b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 26 Dec 2022 20:26:38 +0100 Subject: [PATCH 013/799] Rename get_registry to get_registry_settings --- app/main.f90 | 4 ++-- src/fpm_registry.f90 | 6 +++--- test/fpm_test/test_registry.f90 | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index 26ade218b9..cbc9bdbe8a 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -18,7 +18,7 @@ program main use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update use fpm_os, only: change_directory, get_current_directory -use fpm_registry, only: get_registry +use fpm_registry, only: get_registry_settings implicit none @@ -29,7 +29,7 @@ program main call get_command_line_settings(cmd_settings) -call get_registry(registry_settings, error) +call get_registry_settings(registry_settings, error) call handle_error(error) call get_current_directory(pwd_start, error) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 50dfee46a1..54cf2bcb00 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -4,12 +4,12 @@ module fpm_registry use fpm_error, only: error_t, fatal_error implicit none private - public get_registry + public get_registry_settings contains !> Obtain registry settings and register local or custom registry if such was specified !> in the global config file. - subroutine get_registry(reg_settings, error, custom_path_to_config_file) + subroutine get_registry_settings(reg_settings, error, custom_path_to_config_file) !> Registry settings to be obtained. type(fpm_registry_settings), allocatable, intent(out) :: reg_settings !> Error handling. @@ -42,6 +42,6 @@ subroutine get_registry(reg_settings, error, custom_path_to_config_file) reg_settings%working_dir = path_to_config_file end if - end subroutine get_registry + end subroutine get_registry_settings end module fpm_registry diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index 25b50818bf..922210ce3d 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -1,7 +1,7 @@ module test_registry use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_command_line, only: fpm_registry_settings - use fpm_registry, only: get_registry + use fpm_registry, only: get_registry_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix @@ -46,7 +46,7 @@ subroutine no_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_registry_settings), allocatable :: registry_settings - call get_registry(registry_settings, error, join_path(tmp_folder, config_file_name)) + call get_registry_settings(registry_settings, error, join_path(tmp_folder, config_file_name)) end subroutine no_file @@ -62,7 +62,7 @@ subroutine empty_file(error) path_to_config_file = join_path(tmp_folder, config_file_name) call filewrite(path_to_config_file, ['']) - call get_registry(registry_settings, error, path_to_config_file) + call get_registry_settings(registry_settings, error, path_to_config_file) call os_delete_dir(os_is_unix(), tmp_folder) From 7c86169fb491e6b6806151c1e0ad148ca628b193 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 27 Dec 2022 00:19:26 +0100 Subject: [PATCH 014/799] Remove test and fix docs --- src/fpm_filesystem.F90 | 2 +- test/fpm_test/test_registry.f90 | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 4e542fc2fe..bbeb843d6f 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -963,7 +963,7 @@ subroutine os_delete_dir(unix, dir, echo) end subroutine os_delete_dir -!> Set path prefix to the local folder. Used for installation, registry etc. +!> Determine the path prefix to the local folder. Used for installation, registry etc. function get_local_prefix(os) result(prefix) !> Installation prefix character(len=:), allocatable :: prefix diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index 922210ce3d..c983f56e53 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -21,31 +21,20 @@ subroutine collect_registry(tests) type(unittest_t), allocatable, intent(out) :: tests(:) tests = [ & - & new_unittest('no-tmp-folder', no_tmp_folder), & & new_unittest('no-file', no_file, should_fail=.true.), & & new_unittest('empty-file', empty_file) & ] end subroutine collect_registry - !> Makes sure no `tmp` folder exists, important for other tests. - subroutine no_tmp_folder(error) - - type(error_t), allocatable, intent(out) :: error - - if (is_dir(tmp_folder)) then - call test_failed(error, 'Folder "'//tmp_folder//'" should not exist before test') - return - end if - - end subroutine no_tmp_folder - !> Throw error when custom path to config file was entered but none exists. subroutine no_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_registry_settings), allocatable :: registry_settings + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + call get_registry_settings(registry_settings, error, join_path(tmp_folder, config_file_name)) end subroutine no_file @@ -57,6 +46,8 @@ subroutine empty_file(error) type(fpm_registry_settings), allocatable :: registry_settings character(len=:), allocatable :: path_to_config_file + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + call mkdir(tmp_folder) path_to_config_file = join_path(tmp_folder, config_file_name) From 4705bfe5ea80cf13734fc581cd1368873b16c66d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 27 Dec 2022 00:38:32 +0100 Subject: [PATCH 015/799] Use path to the folder --- src/fpm_registry.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 54cf2bcb00..f2e94d8716 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -1,6 +1,6 @@ module fpm_registry use fpm_command_line, only: fpm_registry_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix + use fpm_filesystem, only: exists, join_path, get_local_prefix, parent_dir use fpm_error, only: error_t, fatal_error implicit none private @@ -39,7 +39,7 @@ subroutine get_registry_settings(reg_settings, error, custom_path_to_config_file ! Obtain registry settings from config file if it was found. if (allocated(path_to_config_file)) then allocate (reg_settings) - reg_settings%working_dir = path_to_config_file + reg_settings%working_dir = parent_dir(path_to_config_file) end if end subroutine get_registry_settings From 239f9088a8840781a70ac5614b442e31f033f9d1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 27 Dec 2022 00:48:12 +0100 Subject: [PATCH 016/799] Improve error message bc it does not have to be config.toml there --- src/fpm_registry.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index f2e94d8716..731933a0c4 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -26,7 +26,7 @@ subroutine get_registry_settings(reg_settings, error, custom_path_to_config_file if (exists(custom_path_to_config_file)) then path_to_config_file = custom_path_to_config_file else - call fatal_error(error, 'No config.toml at: "'//custom_path_to_config_file//'"') + call fatal_error(error, 'No config file at: "'//custom_path_to_config_file//'"') end if else ! Use default path to the config file if it wasn't manually set and exists. From 2a6bfe07e3323ee06a177c686626b7612f6ead6f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 27 Dec 2022 02:35:53 +0100 Subject: [PATCH 017/799] Put in share folder --- src/fpm_registry.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 731933a0c4..0ebf78b0c0 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -30,7 +30,7 @@ subroutine get_registry_settings(reg_settings, error, custom_path_to_config_file end if else ! Use default path to the config file if it wasn't manually set and exists. - default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') + default_path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') if (exists(default_path_to_config_file)) then path_to_config_file = default_path_to_config_file end if From 1f4ea48fddbd017951fb1467f40d7b6bc3c0181d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 31 Dec 2022 00:06:48 +0100 Subject: [PATCH 018/799] Use fpm_global_settings and path --- app/main.f90 | 8 ++++---- src/fpm_command_line.f90 | 9 +++++++-- src/fpm_registry.f90 | 31 ++++++++++++++++--------------- test/fpm_test/main.f90 | 4 ++-- test/fpm_test/test_registry.f90 | 30 +++++++++++++++--------------- 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index cbc9bdbe8a..cc1507d7ac 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -2,7 +2,7 @@ program main use, intrinsic :: iso_fortran_env, only : error_unit, output_unit use fpm_command_line, only: & fpm_cmd_settings, & - fpm_registry_settings, & + fpm_global_settings, & fpm_new_settings, & fpm_build_settings, & fpm_run_settings, & @@ -18,18 +18,18 @@ program main use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update use fpm_os, only: change_directory, get_current_directory -use fpm_registry, only: get_registry_settings +use fpm_settings, only: get_global_settings implicit none class(fpm_cmd_settings), allocatable :: cmd_settings -type(fpm_registry_settings), allocatable :: registry_settings +type(fpm_global_settings), allocatable :: global_settings type(error_t), allocatable :: error character(len=:), allocatable :: pwd_start, pwd_working, working_dir, project_root call get_command_line_settings(cmd_settings) -call get_registry_settings(registry_settings, error) +call get_global_settings(global_settings, error) call handle_error(error) call get_current_directory(pwd_start, error) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index b2d1b3321a..2843a426d7 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -41,7 +41,7 @@ module fpm_command_line private public :: fpm_cmd_settings, & - fpm_registry_settings, & + fpm_global_settings, & fpm_build_settings, & fpm_install_settings, & fpm_new_settings, & @@ -58,10 +58,15 @@ module fpm_command_line integer,parameter :: ibug=4096 +type, extends(fpm_cmd_settings) :: fpm_global_settings + !> Path to the global config file including the file name. + character(len=:), allocatable :: path + type(fpm_registry_settings), allocatable :: registry_settings +end type + type, extends(fpm_cmd_settings) :: fpm_registry_settings character(len=:), allocatable :: path character(len=:), allocatable :: url - contains procedure :: uses_default_registry end type diff --git a/src/fpm_registry.f90 b/src/fpm_registry.f90 index 0ebf78b0c0..6bcd309609 100644 --- a/src/fpm_registry.f90 +++ b/src/fpm_registry.f90 @@ -1,17 +1,17 @@ -module fpm_registry - use fpm_command_line, only: fpm_registry_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix, parent_dir +!> Manages global settings which are defined in the global config file. +module fpm_settings + use fpm_command_line, only: fpm_global_settings + use fpm_filesystem, only: exists, join_path, get_local_prefix use fpm_error, only: error_t, fatal_error implicit none private - public get_registry_settings + public get_global_settings contains - !> Obtain registry settings and register local or custom registry if such was specified - !> in the global config file. - subroutine get_registry_settings(reg_settings, error, custom_path_to_config_file) - !> Registry settings to be obtained. - type(fpm_registry_settings), allocatable, intent(out) :: reg_settings + !> Obtain global settings from the global config file. + subroutine get_global_settings(global_settings, error, custom_path_to_config_file) + !> Global settings to be obtained. + type(fpm_global_settings), allocatable, intent(out) :: global_settings !> Error handling. type(error_t), allocatable, intent(out) :: error !> Custom path to the config file. @@ -26,22 +26,23 @@ subroutine get_registry_settings(reg_settings, error, custom_path_to_config_file if (exists(custom_path_to_config_file)) then path_to_config_file = custom_path_to_config_file else + ! Throw error if specified path doesn't exist. call fatal_error(error, 'No config file at: "'//custom_path_to_config_file//'"') end if else - ! Use default path to the config file if it wasn't manually set and exists. + ! Use default path to the config file if it wasn't specified and exists. default_path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') if (exists(default_path_to_config_file)) then path_to_config_file = default_path_to_config_file end if end if - ! Obtain registry settings from config file if it was found. + ! Set the path to global config file if it was found. if (allocated(path_to_config_file)) then - allocate (reg_settings) - reg_settings%working_dir = parent_dir(path_to_config_file) + allocate (global_settings) + global_settings%path = path_to_config_file end if - end subroutine get_registry_settings + end subroutine get_global_settings -end module fpm_registry +end module fpm_settings diff --git a/test/fpm_test/main.f90 b/test/fpm_test/main.f90 index d47f0ce70c..b2812964f2 100644 --- a/test/fpm_test/main.f90 +++ b/test/fpm_test/main.f90 @@ -12,7 +12,7 @@ program fpm_testing use test_backend, only: collect_backend use test_installer, only : collect_installer use test_versioning, only : collect_versioning - use test_registry, only : collect_registry + use test_settings, only : collect_settings implicit none integer :: stat, is character(len=:), allocatable :: suite_name, test_name @@ -31,7 +31,7 @@ program fpm_testing & new_testsuite("fpm_test_backend", collect_backend), & & new_testsuite("fpm_installer", collect_installer), & & new_testsuite("fpm_versioning", collect_versioning), & - & new_testsuite("fpm_registry", collect_registry) & + & new_testsuite("fpm_settings", collect_settings) & & ] call get_argument(1, suite_name) diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_registry.f90 index c983f56e53..958225c0e7 100644 --- a/test/fpm_test/test_registry.f90 +++ b/test/fpm_test/test_registry.f90 @@ -1,13 +1,13 @@ -module test_registry +module test_settings use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_command_line, only: fpm_registry_settings - use fpm_registry, only: get_registry_settings + use fpm_command_line, only: fpm_global_settings + use fpm_settings, only: get_global_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix implicit none private - public collect_registry + public collect_settings character(len=*), parameter :: tmp_folder = 'tmp' character(len=*), parameter :: config_file_name = 'config.toml' @@ -15,7 +15,7 @@ module test_registry contains !> Collect unit tests. - subroutine collect_registry(tests) + subroutine collect_settings(tests) !> Unit tests to collect. type(unittest_t), allocatable, intent(out) :: tests(:) @@ -25,17 +25,17 @@ subroutine collect_registry(tests) & new_unittest('empty-file', empty_file) & ] - end subroutine collect_registry + end subroutine collect_settings !> Throw error when custom path to config file was entered but none exists. subroutine no_file(error) type(error_t), allocatable, intent(out) :: error - type(fpm_registry_settings), allocatable :: registry_settings + type(fpm_global_settings), allocatable :: global_settings if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - call get_registry_settings(registry_settings, error, join_path(tmp_folder, config_file_name)) + call get_global_settings(global_settings, error, join_path(tmp_folder, config_file_name)) end subroutine no_file @@ -43,7 +43,7 @@ end subroutine no_file subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error - type(fpm_registry_settings), allocatable :: registry_settings + type(fpm_global_settings), allocatable :: global_settings character(len=:), allocatable :: path_to_config_file if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) @@ -53,20 +53,20 @@ subroutine empty_file(error) path_to_config_file = join_path(tmp_folder, config_file_name) call filewrite(path_to_config_file, ['']) - call get_registry_settings(registry_settings, error, path_to_config_file) + call get_global_settings(global_settings, error, path_to_config_file) call os_delete_dir(os_is_unix(), tmp_folder) - if (.not. allocated(registry_settings)) then - call test_failed(error, 'registry_settings not allocated') + if (.not. allocated(global_settings)) then + call test_failed(error, 'global_settings not allocated') return end if - if (.not. allocated(registry_settings%working_dir)) then - call test_failed(error, 'registry_settings%working_dir not allocated') + if (.not. allocated(global_settings%path)) then + call test_failed(error, 'global_settings%path not allocated') return end if end subroutine empty_file -end module test_registry +end module test_settings From 073cb4ba965f865b087e1728140169583aaecdf1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 31 Dec 2022 00:08:58 +0100 Subject: [PATCH 019/799] Rename files separately for easier reviewing :) --- src/{fpm_registry.f90 => fpm_settings.f90} | 0 test/fpm_test/{test_registry.f90 => test_settings.f90} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{fpm_registry.f90 => fpm_settings.f90} (100%) rename test/fpm_test/{test_registry.f90 => test_settings.f90} (100%) diff --git a/src/fpm_registry.f90 b/src/fpm_settings.f90 similarity index 100% rename from src/fpm_registry.f90 rename to src/fpm_settings.f90 diff --git a/test/fpm_test/test_registry.f90 b/test/fpm_test/test_settings.f90 similarity index 100% rename from test/fpm_test/test_registry.f90 rename to test/fpm_test/test_settings.f90 From 044f7e30ddda862e226236611dd24c7545283a78 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 31 Dec 2022 00:29:46 +0100 Subject: [PATCH 020/799] Nit comment --- src/fpm_settings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 6bcd309609..c7233be931 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -37,7 +37,7 @@ subroutine get_global_settings(global_settings, error, custom_path_to_config_fil end if end if - ! Set the path to global config file if it was found. + ! Set the path to the global config file if it was found. if (allocated(path_to_config_file)) then allocate (global_settings) global_settings%path = path_to_config_file From 6295ebe8486e31d4cfe8a337121306f1bae4d04c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 31 Dec 2022 01:43:58 +0100 Subject: [PATCH 021/799] Not use share folder on Windows machines --- src/fpm_settings.f90 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index c7233be931..f4c7383e75 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -2,6 +2,7 @@ module fpm_settings use fpm_command_line, only: fpm_global_settings use fpm_filesystem, only: exists, join_path, get_local_prefix + use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error implicit none private @@ -31,7 +32,11 @@ subroutine get_global_settings(global_settings, error, custom_path_to_config_fil end if else ! Use default path to the config file if it wasn't specified and exists. - default_path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') + if (os_is_unix()) then + default_path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') + else + default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') + end if if (exists(default_path_to_config_file)) then path_to_config_file = default_path_to_config_file end if From b1142c2bb9efd6e1cbded3de37d86bc27555149a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 31 Dec 2022 02:03:05 +0100 Subject: [PATCH 022/799] Eliminate temporary variable --- src/fpm_settings.f90 | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index f4c7383e75..fe8d5cca0e 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -17,8 +17,6 @@ subroutine get_global_settings(global_settings, error, custom_path_to_config_fil type(error_t), allocatable, intent(out) :: error !> Custom path to the config file. character(len=*), optional, intent(in) :: custom_path_to_config_file - !> System-dependent default path to the config file. - character(len=:), allocatable :: default_path_to_config_file !> Final path to the config file. character(len=:), allocatable :: path_to_config_file @@ -29,24 +27,22 @@ subroutine get_global_settings(global_settings, error, custom_path_to_config_fil else ! Throw error if specified path doesn't exist. call fatal_error(error, 'No config file at: "'//custom_path_to_config_file//'"') + return end if else - ! Use default path to the config file if it wasn't specified and exists. + ! Use default paths to the config file if it wasn't specified. if (os_is_unix()) then - default_path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') + path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') else - default_path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') - end if - if (exists(default_path_to_config_file)) then - path_to_config_file = default_path_to_config_file + path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') end if + ! Return quietly (not set the path) if the config file doesn't exist. + if (.not. exists(path_to_config_file)) return end if - ! Set the path to the global config file if it was found. - if (allocated(path_to_config_file)) then - allocate (global_settings) - global_settings%path = path_to_config_file - end if + ! Set the path to the global config file. + allocate (global_settings) + global_settings%path = path_to_config_file end subroutine get_global_settings From 281e6a472f2c259abbf556b81d8d25aaec319cc8 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 2 Jan 2023 02:16:02 +0100 Subject: [PATCH 023/799] Parse path and url registry settings and add tests --- src/fpm/cmd/new.f90 | 2 +- src/fpm_settings.f90 | 75 +++++++++++++++++- test/fpm_test/test_settings.f90 | 135 ++++++++++++++++++++++++++++++-- 3 files changed, 205 insertions(+), 7 deletions(-) diff --git a/src/fpm/cmd/new.f90 b/src/fpm/cmd/new.f90 index bed0980553..ddec9e35b8 100644 --- a/src/fpm/cmd/new.f90 +++ b/src/fpm/cmd/new.f90 @@ -56,7 +56,7 @@ module fpm_cmd_new use fpm_command_line, only : fpm_new_settings use fpm_environment, only : OS_LINUX, OS_MACOS, OS_WINDOWS use fpm_filesystem, only : join_path, exists, basename, mkdir, is_dir -use fpm_filesystem, only : fileopen, fileclose, filewrite, warnwrite, which, run +use fpm_filesystem, only : fileopen, fileclose, warnwrite, which, run use fpm_strings, only : join, to_fortran_name use fpm_error, only : fpm_stop diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index fe8d5cca0e..d2acc250a4 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -4,6 +4,8 @@ module fpm_settings use fpm_filesystem, only: exists, join_path, get_local_prefix use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error + use fpm_toml, only: toml_table, toml_error, toml_stat, get_value + use tomlf, only: toml_load implicit none private public get_global_settings @@ -13,12 +15,16 @@ module fpm_settings subroutine get_global_settings(global_settings, error, custom_path_to_config_file) !> Global settings to be obtained. type(fpm_global_settings), allocatable, intent(out) :: global_settings - !> Error handling. + !> Error reading config file. type(error_t), allocatable, intent(out) :: error !> Custom path to the config file. character(len=*), optional, intent(in) :: custom_path_to_config_file !> Final path to the config file. character(len=:), allocatable :: path_to_config_file + !> TOML table to be filled with global config settings. + type(toml_table), allocatable :: table + !> Error parsing to TOML table. + type(toml_error), allocatable :: parse_error ! Use custom path to the config file if it was specified. if (present(custom_path_to_config_file)) then @@ -44,6 +50,73 @@ subroutine get_global_settings(global_settings, error, custom_path_to_config_fil allocate (global_settings) global_settings%path = path_to_config_file + ! Load into TOML table. + call toml_load(table, path_to_config_file, error=parse_error) + + if (allocated(parse_error)) then + allocate (error) + call move_alloc(parse_error%message, error%message) + return + end if + + call get_registry_settings(global_settings, table, error) + end subroutine get_global_settings + subroutine get_registry_settings(global_settings, table, error) + type(fpm_global_settings), intent(inout) :: global_settings + type(toml_table), intent(inout) :: table + type(error_t), allocatable, intent(out) :: error + type(toml_table), pointer :: child + character(:), allocatable :: path, url + integer :: stat + + call get_value(table, 'registry', child, requested=.false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error reading registry from config file "'// & + global_settings%path//'"') + return + end if + + ! Quietly return if no registry table was found. + if (.not. associated(child)) return + + allocate (global_settings%registry_settings) + + call get_value(child, 'path', path, stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error parsing path to registry: "'//path//'"') + return + end if + + if (allocated(path)) then + if (.not. exists(path)) then + call fatal_error(error, "Path to registry doesn't exist:"//'"'//path//'"') + return + end if + + global_settings%registry_settings%path = path + end if + + call get_value(child, 'url', url, stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error parsing url to registry: "'//url//'"') + return + end if + + if (allocated(url)) then + ! Throw error when both path and url were provided. + if (allocated(path)) then + call fatal_error(error, 'Do not provide both path and url to registry') + return + end if + + global_settings%registry_settings%url = url + end if + + end subroutine get_registry_settings + end module fpm_settings diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 958225c0e7..321ab8853b 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -4,6 +4,7 @@ module test_settings use fpm_settings, only: get_global_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix + use fpm_toml, only: new_table implicit none private @@ -22,19 +23,27 @@ subroutine collect_settings(tests) tests = [ & & new_unittest('no-file', no_file, should_fail=.true.), & - & new_unittest('empty-file', empty_file) & + & new_unittest('empty-file', empty_file), & + & new_unittest('empty-registry-table', empty_registry_table), & + & new_unittest('has-non-existent-path-registry', has_non_existent_path_registry, should_fail=.true.), & + & new_unittest('has-existent-path-registry', has_existent_path_registry), & + & new_unittest('has-url-registry', has_url_registry), & + & new_unittest('has-both-path-and-url-registry', has_both_path_and_url_registry, should_fail=.true.) & ] end subroutine collect_settings + subroutine delete_tmp_folder + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + end + !> Throw error when custom path to config file was entered but none exists. subroutine no_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - + call delete_tmp_folder() call get_global_settings(global_settings, error, join_path(tmp_folder, config_file_name)) end subroutine no_file @@ -46,8 +55,7 @@ subroutine empty_file(error) type(fpm_global_settings), allocatable :: global_settings character(len=:), allocatable :: path_to_config_file - if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - + call delete_tmp_folder() call mkdir(tmp_folder) path_to_config_file = join_path(tmp_folder, config_file_name) @@ -67,6 +75,123 @@ subroutine empty_file(error) return end if + if (allocated(global_settings%registry_settings)) then + call test_failed(error, 'global_settings%registry_settings should not be allocated') + return + end if + end subroutine empty_file + subroutine empty_registry_table(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['[registry]']) + + call get_global_settings(global_settings, error, path_to_config_file) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'global_settings%registry_settings not allocated') + return + end if + + if (allocated(global_settings%registry_settings%path)) then + call test_failed(error, "Path shouldn't be allocated") + return + end if + + if (allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url shouldn't be allocated") + return + end if + + end subroutine + + subroutine has_non_existent_path_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['[registry]', 'path="abc"']) + call get_global_settings(global_settings, error, path_to_config_file) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + subroutine has_existent_path_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['[registry]', 'path="tmp"']) + + call get_global_settings(global_settings, error, path_to_config_file) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if + + if (allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url shouldn't be allocated") + return + end if + end subroutine + + subroutine has_url_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['[registry]', 'url="http"']) + + call get_global_settings(global_settings, error, path_to_config_file) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (allocated(global_settings%registry_settings%path)) then + call test_failed(error, "Path shouldn't be allocated") + return + end if + + if (.not. allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url not allocated") + return + end if + end subroutine + + subroutine has_both_path_and_url_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['[registry]', 'path="tmp"', 'url="http"']) + call get_global_settings(global_settings, error, path_to_config_file) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + end module test_settings From eb180e12532581fa9a9feb963eccc545659211bb Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 2 Jan 2023 02:31:14 +0100 Subject: [PATCH 024/799] Improve naming --- test/fpm_test/test_settings.f90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 321ab8853b..ef3c406043 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -25,10 +25,10 @@ subroutine collect_settings(tests) & new_unittest('no-file', no_file, should_fail=.true.), & & new_unittest('empty-file', empty_file), & & new_unittest('empty-registry-table', empty_registry_table), & - & new_unittest('has-non-existent-path-registry', has_non_existent_path_registry, should_fail=.true.), & - & new_unittest('has-existent-path-registry', has_existent_path_registry), & - & new_unittest('has-url-registry', has_url_registry), & - & new_unittest('has-both-path-and-url-registry', has_both_path_and_url_registry, should_fail=.true.) & + & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & + & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & + & new_unittest('has-url-to-registry', has_url_to_registry), & + & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.) & ] end subroutine collect_settings @@ -114,7 +114,7 @@ subroutine empty_registry_table(error) end subroutine - subroutine has_non_existent_path_registry(error) + subroutine has_non_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings character(len=:), allocatable :: path_to_config_file @@ -128,7 +128,7 @@ subroutine has_non_existent_path_registry(error) call os_delete_dir(os_is_unix(), tmp_folder) end subroutine - subroutine has_existent_path_registry(error) + subroutine has_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings character(len=:), allocatable :: path_to_config_file @@ -154,7 +154,7 @@ subroutine has_existent_path_registry(error) end if end subroutine - subroutine has_url_registry(error) + subroutine has_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings character(len=:), allocatable :: path_to_config_file @@ -180,7 +180,7 @@ subroutine has_url_registry(error) end if end subroutine - subroutine has_both_path_and_url_registry(error) + subroutine has_both_path_and_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings character(len=:), allocatable :: path_to_config_file From 4a25a123d85f6e8f92584bb4a3ee00d6beb02d4d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 2 Jan 2023 18:45:15 +0100 Subject: [PATCH 025/799] Add is_absolute_path to fpm_filesystem with tests --- src/fpm_filesystem.F90 | 31 ++++++++--- src/fpm_settings.f90 | 2 +- test/fpm_test/test_filesystem.f90 | 92 +++++++++++++++++++++++++++++-- test/fpm_test/test_settings.f90 | 2 +- 4 files changed, 112 insertions(+), 15 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index bbeb843d6f..745aa0f18e 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -12,13 +12,9 @@ module fpm_filesystem implicit none private public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & - mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file - public :: fileopen, fileclose, filewrite, warnwrite, parent_dir - public :: is_hidden_file - public :: read_lines, read_lines_expanded - public :: which, run, LINE_BUFFER_LEN - public :: os_delete_dir - + mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & + filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & + LINE_BUFFER_LEN, os_delete_dir, is_absolute_path integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -995,4 +991,25 @@ function get_local_prefix(os) result(prefix) end function get_local_prefix + !> Returns .true. if provided path is absolute. + logical function is_absolute_path(path, is_unix) + character(len=*), intent(in) :: path + logical, optional, intent(in):: is_unix + character(len=*), parameter :: letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + logical :: is_unix_os + + if (present(is_unix)) then + is_unix_os = is_unix + else + is_unix_os = os_is_unix() + end if + + if (is_unix_os) then + is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' + else + is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' + end if + + end function is_absolute_path + end module fpm_filesystem diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index d2acc250a4..1aa2015cd7 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -8,7 +8,7 @@ module fpm_settings use tomlf, only: toml_load implicit none private - public get_global_settings + public :: get_global_settings contains !> Obtain global settings from the global config file. diff --git a/test/fpm_test/test_filesystem.f90 b/test/fpm_test/test_filesystem.f90 index b6b7681706..f038dcec02 100644 --- a/test/fpm_test/test_filesystem.f90 +++ b/test/fpm_test/test_filesystem.f90 @@ -1,7 +1,7 @@ module test_filesystem use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: canon_path, is_dir, mkdir, os_delete_dir, & - join_path + join_path, is_absolute_path use fpm_environment, only: OS_WINDOWS, get_os_type, os_is_unix implicit none private @@ -12,14 +12,15 @@ module test_filesystem !> Collect all exported unit tests - subroutine collect_filesystem(testsuite) + subroutine collect_filesystem(tests) !> Collection of tests - type(unittest_t), allocatable, intent(out) :: testsuite(:) + type(unittest_t), allocatable, intent(out) :: tests(:) - testsuite = [ & + tests = [ & & new_unittest("canon-path", test_canon_path), & - & new_unittest("create-delete-directory", test_mkdir_rmdir) & + & new_unittest("create-delete-directory", test_mkdir_rmdir), & + & new_unittest("test-is-absolute-path", test_is_absolute_path) & ] end subroutine collect_filesystem @@ -173,7 +174,86 @@ subroutine check_rmdir(error, path) "Directory path "//path//" cannot be deleted") end if - end subroutine check_rmdir + end subroutine check_rmdir + subroutine test_is_absolute_path(error) + type(error_t), allocatable, intent(out) :: error + + if (is_absolute_path('.', is_unix=.true.)) then + call test_failed(error, "Relative path '.' isn't absolute") + return + end if + + if (is_absolute_path('abc', is_unix=.true.)) then + call test_failed(error, "Relative path 'abc' isn't absolute") + return + end if + + if (.not. is_absolute_path('/', is_unix=.true.)) then + call test_failed(error, "Path '/' is absolute") + return + end if + + if (.not. is_absolute_path('/abc', is_unix=.true.)) then + call test_failed(error, "Path '/abc' is absolute") + return + end if + + if (.not. is_absolute_path('~/', is_unix=.true.)) then + call test_failed(error, "Path '~/' is absolute") + return + end if + + if (.not. is_absolute_path('~/', is_unix=.true.)) then + call test_failed(error, "Path '~/' is absolute") + return + end if + + if (is_absolute_path('abc', is_unix=.false.)) then + call test_failed(error, "Relative path 'abc' isn't absolute") + return + end if + + if (is_absolute_path('..', is_unix=.false.)) then + call test_failed(error, "Relative path '..' isn't absolute") + return + end if + + if (is_absolute_path('abc', is_unix=.false.)) then + call test_failed(error, "Relative path 'abc' isn't absolute") + return + end if + + if (is_absolute_path('/', is_unix=.false.)) then + call test_failed(error, "Path '/' isn't absolute on Windows") + return + end if + + if (is_absolute_path('c/', is_unix=.false.)) then + call test_failed(error, "Path 'c/' isn't absolute") + return + end if + + if (.not. is_absolute_path('C:', is_unix=.false.)) then + call test_failed(error, "Path 'C:' is absolute") + return + end if + + if (.not. is_absolute_path('x:', is_unix=.false.)) then + call test_failed(error, "Path 'x:' is absolute") + return + end if + + if (.not. is_absolute_path('x:xyz', is_unix=.false.)) then + call test_failed(error, "Path 'x:xyz' is absolute") + return + end if + + if (is_absolute_path('1:', is_unix=.false.)) then + call test_failed(error, "Path '1:' isn't absolute") + return + end if + + end subroutine test_is_absolute_path end module test_filesystem diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index ef3c406043..f729a78d94 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -8,7 +8,7 @@ module test_settings implicit none private - public collect_settings + public :: collect_settings character(len=*), parameter :: tmp_folder = 'tmp' character(len=*), parameter :: config_file_name = 'config.toml' From 1ddb4f479bd88ca73d304aec079e00d744df50fc Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 3 Jan 2023 03:29:53 +0100 Subject: [PATCH 026/799] Remove is_absolute_path again, add get_absolute_path and use for path registry with tests --- src/fpm_filesystem.F90 | 23 +-------- src/fpm_os.F90 | 20 +++++++- src/fpm_settings.f90 | 8 ++- test/fpm_test/test_filesystem.f90 | 85 +------------------------------ test/fpm_test/test_settings.f90 | 82 +++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 108 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 745aa0f18e..efe134a9bf 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -14,7 +14,7 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path + LINE_BUFFER_LEN, os_delete_dir integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -991,25 +991,4 @@ function get_local_prefix(os) result(prefix) end function get_local_prefix - !> Returns .true. if provided path is absolute. - logical function is_absolute_path(path, is_unix) - character(len=*), intent(in) :: path - logical, optional, intent(in):: is_unix - character(len=*), parameter :: letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - logical :: is_unix_os - - if (present(is_unix)) then - is_unix_os = is_unix - else - is_unix_os = os_is_unix() - end if - - if (is_unix_os) then - is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' - else - is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' - end if - - end function is_absolute_path - end module fpm_filesystem diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 71663fe17c..3a1dae2bb4 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -3,7 +3,7 @@ module fpm_os use fpm_error, only : error_t, fatal_error implicit none private - public :: change_directory, get_current_directory + public :: change_directory, get_current_directory, get_absolute_path #ifndef _WIN32 character(len=*), parameter :: pwd_env = "PWD" @@ -102,4 +102,22 @@ subroutine c_f_character(rhs, lhs) end subroutine c_f_character + !> Get absolute path from a path. + subroutine get_absolute_path(path, abs_path, error) + character(len=*), intent(in) :: path + character(len=:), allocatable, intent(out) :: abs_path + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: start_dir, target_dir + + call get_current_directory(start_dir, error) + if (allocated(error)) return + call change_directory(path, error) + if (allocated(error)) return + call get_current_directory(target_dir, error) + if (allocated(error)) return + call change_directory(start_dir, error) + if (allocated(error)) return + abs_path = target_dir + end subroutine get_absolute_path + end module fpm_os diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 1aa2015cd7..441c26dab4 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -5,6 +5,7 @@ module fpm_settings use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value + use fpm_os, only: get_current_directory, change_directory, get_absolute_path use tomlf, only: toml_load implicit none private @@ -68,7 +69,7 @@ subroutine get_registry_settings(global_settings, table, error) type(toml_table), intent(inout) :: table type(error_t), allocatable, intent(out) :: error type(toml_table), pointer :: child - character(:), allocatable :: path, url + character(:), allocatable :: path, url, abs_path integer :: stat call get_value(table, 'registry', child, requested=.false., stat=stat) @@ -97,7 +98,10 @@ subroutine get_registry_settings(global_settings, table, error) return end if - global_settings%registry_settings%path = path + ! Making sure that path is absolute + call get_absolute_path(path, abs_path, error) + if (allocated(error)) return + global_settings%registry_settings%path = abs_path end if call get_value(child, 'url', url, stat=stat) diff --git a/test/fpm_test/test_filesystem.f90 b/test/fpm_test/test_filesystem.f90 index f038dcec02..602b61de3f 100644 --- a/test/fpm_test/test_filesystem.f90 +++ b/test/fpm_test/test_filesystem.f90 @@ -1,7 +1,7 @@ module test_filesystem use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: canon_path, is_dir, mkdir, os_delete_dir, & - join_path, is_absolute_path + join_path use fpm_environment, only: OS_WINDOWS, get_os_type, os_is_unix implicit none private @@ -19,8 +19,7 @@ subroutine collect_filesystem(tests) tests = [ & & new_unittest("canon-path", test_canon_path), & - & new_unittest("create-delete-directory", test_mkdir_rmdir), & - & new_unittest("test-is-absolute-path", test_is_absolute_path) & + & new_unittest("create-delete-directory", test_mkdir_rmdir) & ] end subroutine collect_filesystem @@ -176,84 +175,4 @@ subroutine check_rmdir(error, path) end subroutine check_rmdir - subroutine test_is_absolute_path(error) - type(error_t), allocatable, intent(out) :: error - - if (is_absolute_path('.', is_unix=.true.)) then - call test_failed(error, "Relative path '.' isn't absolute") - return - end if - - if (is_absolute_path('abc', is_unix=.true.)) then - call test_failed(error, "Relative path 'abc' isn't absolute") - return - end if - - if (.not. is_absolute_path('/', is_unix=.true.)) then - call test_failed(error, "Path '/' is absolute") - return - end if - - if (.not. is_absolute_path('/abc', is_unix=.true.)) then - call test_failed(error, "Path '/abc' is absolute") - return - end if - - if (.not. is_absolute_path('~/', is_unix=.true.)) then - call test_failed(error, "Path '~/' is absolute") - return - end if - - if (.not. is_absolute_path('~/', is_unix=.true.)) then - call test_failed(error, "Path '~/' is absolute") - return - end if - - if (is_absolute_path('abc', is_unix=.false.)) then - call test_failed(error, "Relative path 'abc' isn't absolute") - return - end if - - if (is_absolute_path('..', is_unix=.false.)) then - call test_failed(error, "Relative path '..' isn't absolute") - return - end if - - if (is_absolute_path('abc', is_unix=.false.)) then - call test_failed(error, "Relative path 'abc' isn't absolute") - return - end if - - if (is_absolute_path('/', is_unix=.false.)) then - call test_failed(error, "Path '/' isn't absolute on Windows") - return - end if - - if (is_absolute_path('c/', is_unix=.false.)) then - call test_failed(error, "Path 'c/' isn't absolute") - return - end if - - if (.not. is_absolute_path('C:', is_unix=.false.)) then - call test_failed(error, "Path 'C:' is absolute") - return - end if - - if (.not. is_absolute_path('x:', is_unix=.false.)) then - call test_failed(error, "Path 'x:' is absolute") - return - end if - - if (.not. is_absolute_path('x:xyz', is_unix=.false.)) then - call test_failed(error, "Path 'x:xyz' is absolute") - return - end if - - if (is_absolute_path('1:', is_unix=.false.)) then - call test_failed(error, "Path '1:' isn't absolute") - return - end if - - end subroutine test_is_absolute_path - end module test_filesystem diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index f729a78d94..1dded60277 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -5,6 +5,7 @@ module test_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix use fpm_toml, only: new_table + use fpm_os, only: get_absolute_path implicit none private @@ -27,6 +28,9 @@ subroutine collect_settings(tests) & new_unittest('empty-registry-table', empty_registry_table), & & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & + & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & + & new_unittest('relative-path-to-registry', relative_path_to_registry), & + & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & & new_unittest('has-url-to-registry', has_url_to_registry), & & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.) & ] @@ -154,6 +158,84 @@ subroutine has_existent_path_to_registry(error) end if end subroutine + subroutine absolute_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file, abs_path + + call delete_tmp_folder() + call mkdir(tmp_folder) + + call get_absolute_path(tmp_folder, abs_path, error) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, [character(len=80) :: '[registry]', 'path="'//abs_path//'"']) + + call get_global_settings(global_settings, error, path_to_config_file) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if + + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine + + subroutine relative_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file, abs_path + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, ['[registry]', 'path="tmp"']) + + call get_global_settings(global_settings, error, path_to_config_file) + + call get_absolute_path(tmp_folder, abs_path, error) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (allocated(error)) return + + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine + + subroutine canonical_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: path_to_config_file, abs_path + + call delete_tmp_folder() + call mkdir(tmp_folder) + + path_to_config_file = join_path(tmp_folder, config_file_name) + call filewrite(path_to_config_file, [character(len=80) :: '[registry]', 'path="./tmp"']) + + call get_global_settings(global_settings, error, path_to_config_file) + + call get_absolute_path(tmp_folder, abs_path, error) + + call os_delete_dir(os_is_unix(), tmp_folder) + + if (allocated(error)) return + + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine + subroutine has_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings From ff4ed9cce695d63b6da7d6e2b93869a5f9fd78f5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 3 Jan 2023 03:49:53 +0100 Subject: [PATCH 027/799] Try identifying error --- test/fpm_test/test_settings.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 1dded60277..308034cc29 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -175,6 +175,8 @@ subroutine absolute_path_to_registry(error) call os_delete_dir(os_is_unix(), tmp_folder) + if (allocated(error)) return + if (.not. allocated(global_settings%registry_settings%path)) then call test_failed(error, 'Path not allocated') return From 98f34efa3413cb1f24a0976be4868e8bec082e5a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 3 Jan 2023 04:16:28 +0100 Subject: [PATCH 028/799] Use single quotes in TOML --- test/fpm_test/test_settings.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 308034cc29..3a9633309f 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -168,8 +168,10 @@ subroutine absolute_path_to_registry(error) call get_absolute_path(tmp_folder, abs_path, error) + if (allocated(error)) return + path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, [character(len=80) :: '[registry]', 'path="'//abs_path//'"']) + call filewrite(path_to_config_file, [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) call get_global_settings(global_settings, error, path_to_config_file) @@ -222,7 +224,7 @@ subroutine canonical_path_to_registry(error) call mkdir(tmp_folder) path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, [character(len=80) :: '[registry]', 'path="./tmp"']) + call filewrite(path_to_config_file, [character(len=80) :: '[registry]', "path='"//join_path('.', 'tmp')//"'"]) call get_global_settings(global_settings, error, path_to_config_file) From 4f246f92fe77b8ee3577763e1f06e210bfcb8fd0 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 6 Jan 2023 19:44:30 +0100 Subject: [PATCH 029/799] Fix relative paths, improve global_settings api and add is_absolute_path back again --- src/fpm_command_line.f90 | 19 ++++- src/fpm_filesystem.F90 | 23 +++++- src/fpm_os.F90 | 13 ++-- src/fpm_settings.f90 | 84 ++++++++++++-------- test/fpm_test/test_filesystem.f90 | 85 ++++++++++++++++++++- test/fpm_test/test_settings.f90 | 122 +++++++++++++++++------------- 6 files changed, 249 insertions(+), 97 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 2843a426d7..96ba35e5e7 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -29,7 +29,7 @@ module fpm_command_line use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified use M_CLI2, only : get_subcommand, CLI_RESPONSE_FILE use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name -use fpm_filesystem, only : basename, canon_path, which, run +use fpm_filesystem, only : basename, canon_path, which, run, join_path use fpm_environment, only : get_command_arguments_quoted use fpm_error, only : fpm_stop, error_t use fpm_os, only : get_current_directory @@ -59,9 +59,13 @@ module fpm_command_line integer,parameter :: ibug=4096 type, extends(fpm_cmd_settings) :: fpm_global_settings - !> Path to the global config file including the file name. - character(len=:), allocatable :: path + !> Path to the global config file excluding the file name. + character(len=:), allocatable :: path_to_folder + !> Name of the global config file. The default is `config.toml`. + character(len=:), allocatable :: file_name type(fpm_registry_settings), allocatable :: registry_settings +contains + procedure :: full_path end type type, extends(fpm_cmd_settings) :: fpm_registry_settings @@ -1337,9 +1341,18 @@ function get_fpm_env(env, default) result(val) val = get_env(fpm_prefix//env, default) end function get_fpm_env + !> The full path to the global config file. + function full_path(self) result(result) + class(fpm_global_settings), intent(in) :: self + character(len=:), allocatable :: result + + result = join_path(self%path_to_folder, self%file_name) + end function + !> The official registry is used by default when no local or custom registry was specified. pure logical function uses_default_registry(self) class(fpm_registry_settings), intent(in) :: self + uses_default_registry = .not. allocated(self%path) .and. .not. allocated(self%url) end function diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index efe134a9bf..745aa0f18e 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -14,7 +14,7 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir + LINE_BUFFER_LEN, os_delete_dir, is_absolute_path integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -991,4 +991,25 @@ function get_local_prefix(os) result(prefix) end function get_local_prefix + !> Returns .true. if provided path is absolute. + logical function is_absolute_path(path, is_unix) + character(len=*), intent(in) :: path + logical, optional, intent(in):: is_unix + character(len=*), parameter :: letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + logical :: is_unix_os + + if (present(is_unix)) then + is_unix_os = is_unix + else + is_unix_os = os_is_unix() + end if + + if (is_unix_os) then + is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' + else + is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' + end if + + end function is_absolute_path + end module fpm_filesystem diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 3a1dae2bb4..3a4ac7cbb7 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -102,22 +102,21 @@ subroutine c_f_character(rhs, lhs) end subroutine c_f_character - !> Get absolute path from a path. - subroutine get_absolute_path(path, abs_path, error) - character(len=*), intent(in) :: path + !> Determine the absolute from the relative path. + subroutine get_absolute_path(rel_path, abs_path, error) + character(len=*), intent(in) :: rel_path character(len=:), allocatable, intent(out) :: abs_path type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: start_dir, target_dir + character(len=:), allocatable :: start_dir call get_current_directory(start_dir, error) if (allocated(error)) return - call change_directory(path, error) + call change_directory(rel_path, error) if (allocated(error)) return - call get_current_directory(target_dir, error) + call get_current_directory(abs_path, error) if (allocated(error)) return call change_directory(start_dir, error) if (allocated(error)) return - abs_path = target_dir end subroutine get_absolute_path end module fpm_os diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 441c26dab4..3f1764f000 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,7 +1,7 @@ !> Manages global settings which are defined in the global config file. module fpm_settings use fpm_command_line, only: fpm_global_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix + use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path, canon_path use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value @@ -13,46 +13,66 @@ module fpm_settings contains !> Obtain global settings from the global config file. - subroutine get_global_settings(global_settings, error, custom_path_to_config_file) + subroutine get_global_settings(global_settings, error) !> Global settings to be obtained. - type(fpm_global_settings), allocatable, intent(out) :: global_settings + type(fpm_global_settings), allocatable, intent(inout) :: global_settings !> Error reading config file. type(error_t), allocatable, intent(out) :: error - !> Custom path to the config file. - character(len=*), optional, intent(in) :: custom_path_to_config_file - !> Final path to the config file. - character(len=:), allocatable :: path_to_config_file + !> Absolute path to the config file. + character(len=:), allocatable :: abs_path_to_config !> TOML table to be filled with global config settings. type(toml_table), allocatable :: table !> Error parsing to TOML table. type(toml_error), allocatable :: parse_error + if (.not. allocated(global_settings)) allocate (global_settings) + ! Use custom path to the config file if it was specified. - if (present(custom_path_to_config_file)) then - if (exists(custom_path_to_config_file)) then - path_to_config_file = custom_path_to_config_file - else - ! Throw error if specified path doesn't exist. - call fatal_error(error, 'No config file at: "'//custom_path_to_config_file//'"') + if (allocated(global_settings%path_to_folder) .and. allocated(global_settings%file_name)) then + ! Throw error if folder doesn't exist. + if (.not. exists(global_settings%path_to_folder)) then + call fatal_error(error, 'Folder not found: "'//global_settings%path_to_folder//'"') + return + end if + + ! Throw error if file doesn't exist. + if (.not. exists(global_settings%full_path())) then + call fatal_error(error, 'File not found: "'//global_settings%full_path()//'"') return end if + + ! Make sure that the path to the global config file is absolute. + call get_absolute_path(global_settings%path_to_folder, abs_path_to_config, error) + if (allocated(error)) return + + global_settings%path_to_folder = abs_path_to_config else - ! Use default paths to the config file if it wasn't specified. + ! Use default path if it wasn't specified. if (os_is_unix()) then - path_to_config_file = join_path(get_local_prefix(), 'share', 'fpm', 'config.toml') + global_settings%path_to_folder = join_path(get_local_prefix(), 'share', 'fpm') else - path_to_config_file = join_path(get_local_prefix(), 'fpm', 'config.toml') + global_settings%path_to_folder = join_path(get_local_prefix(), 'fpm') end if - ! Return quietly (not set the path) if the config file doesn't exist. - if (.not. exists(path_to_config_file)) return - end if - ! Set the path to the global config file. - allocate (global_settings) - global_settings%path = path_to_config_file + ! Use default file name. + global_settings%file_name = 'config.toml' + + ! Deallocate and return if path doesn't exist. + if (.not. exists(global_settings%path_to_folder)) then + deallocate (global_settings%path_to_folder) + deallocate (global_settings%file_name) + return + end if + + ! Deallocate name and return if the config file doesn't exist. + if (.not. exists(global_settings%full_path())) then + deallocate (global_settings%file_name) + return + end if + end if ! Load into TOML table. - call toml_load(table, path_to_config_file, error=parse_error) + call toml_load(table, global_settings%full_path(), error=parse_error) if (allocated(parse_error)) then allocate (error) @@ -60,6 +80,7 @@ subroutine get_global_settings(global_settings, error, custom_path_to_config_fil return end if + ! Read registry subtable. call get_registry_settings(global_settings, table, error) end subroutine get_global_settings @@ -76,7 +97,7 @@ subroutine get_registry_settings(global_settings, table, error) if (stat /= toml_stat%success) then call fatal_error(error, 'Error reading registry from config file "'// & - global_settings%path//'"') + global_settings%full_path()//'"') return end if @@ -93,14 +114,17 @@ subroutine get_registry_settings(global_settings, table, error) end if if (allocated(path)) then - if (.not. exists(path)) then - call fatal_error(error, "Path to registry doesn't exist:"//'"'//path//'"') + if (is_absolute_path(path)) then + abs_path = path + else + abs_path = canon_path(join_path(global_settings%path_to_folder, path)) + end if + + if (.not. exists(abs_path)) then + call fatal_error(error, "No registry at: '"//abs_path//"'") return end if - ! Making sure that path is absolute - call get_absolute_path(path, abs_path, error) - if (allocated(error)) return global_settings%registry_settings%path = abs_path end if @@ -114,7 +138,7 @@ subroutine get_registry_settings(global_settings, table, error) if (allocated(url)) then ! Throw error when both path and url were provided. if (allocated(path)) then - call fatal_error(error, 'Do not provide both path and url to registry') + call fatal_error(error, 'Do not provide both path and url to the registry') return end if diff --git a/test/fpm_test/test_filesystem.f90 b/test/fpm_test/test_filesystem.f90 index 602b61de3f..ed3f17e5be 100644 --- a/test/fpm_test/test_filesystem.f90 +++ b/test/fpm_test/test_filesystem.f90 @@ -1,7 +1,7 @@ module test_filesystem use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: canon_path, is_dir, mkdir, os_delete_dir, & - join_path + join_path, is_absolute_path use fpm_environment, only: OS_WINDOWS, get_os_type, os_is_unix implicit none private @@ -19,7 +19,8 @@ subroutine collect_filesystem(tests) tests = [ & & new_unittest("canon-path", test_canon_path), & - & new_unittest("create-delete-directory", test_mkdir_rmdir) & + & new_unittest("create-delete-directory", test_mkdir_rmdir), & + & new_unittest("test-is-absolute-path", test_is_absolute_path) & ] end subroutine collect_filesystem @@ -175,4 +176,84 @@ subroutine check_rmdir(error, path) end subroutine check_rmdir + subroutine test_is_absolute_path(error) + type(error_t), allocatable, intent(out) :: error + + if (is_absolute_path('.', is_unix=.true.)) then + call test_failed(error, "Relative path '.' isn't absolute") + return + end if + + if (is_absolute_path('abc', is_unix=.true.)) then + call test_failed(error, "Relative path 'abc' isn't absolute") + return + end if + + if (.not. is_absolute_path('/', is_unix=.true.)) then + call test_failed(error, "Path '/' is absolute") + return + end if + + if (.not. is_absolute_path('/abc', is_unix=.true.)) then + call test_failed(error, "Path '/abc' is absolute") + return + end if + + if (.not. is_absolute_path('~/', is_unix=.true.)) then + call test_failed(error, "Path '~/' is absolute") + return + end if + + if (.not. is_absolute_path('~/', is_unix=.true.)) then + call test_failed(error, "Path '~/' is absolute") + return + end if + + if (is_absolute_path('abc', is_unix=.false.)) then + call test_failed(error, "Relative path 'abc' isn't absolute") + return + end if + + if (is_absolute_path('..', is_unix=.false.)) then + call test_failed(error, "Relative path '..' isn't absolute") + return + end if + + if (is_absolute_path('abc', is_unix=.false.)) then + call test_failed(error, "Relative path 'abc' isn't absolute") + return + end if + + if (is_absolute_path('/', is_unix=.false.)) then + call test_failed(error, "Path '/' isn't absolute on Windows") + return + end if + + if (is_absolute_path('c/', is_unix=.false.)) then + call test_failed(error, "Path 'c/' isn't absolute") + return + end if + + if (.not. is_absolute_path('C:', is_unix=.false.)) then + call test_failed(error, "Path 'C:' is absolute") + return + end if + + if (.not. is_absolute_path('x:', is_unix=.false.)) then + call test_failed(error, "Path 'x:' is absolute") + return + end if + + if (.not. is_absolute_path('x:xyz', is_unix=.false.)) then + call test_failed(error, "Path 'x:xyz' is absolute") + return + end if + + if (is_absolute_path('1:', is_unix=.false.)) then + call test_failed(error, "Path '1:' isn't absolute") + return + end if + + end subroutine test_is_absolute_path + end module test_filesystem diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 3a9633309f..41555ee5ae 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -23,6 +23,7 @@ subroutine collect_settings(tests) type(unittest_t), allocatable, intent(out) :: tests(:) tests = [ & + & new_unittest('no-folder', no_folder, should_fail=.true.), & & new_unittest('no-file', no_file, should_fail=.true.), & & new_unittest('empty-file', empty_file), & & new_unittest('empty-registry-table', empty_registry_table), & @@ -41,66 +42,74 @@ subroutine delete_tmp_folder if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) end - !> Throw error when custom path to config file was entered but none exists. - subroutine no_file(error) + subroutine setup_global_settings(global_settings) + type(fpm_global_settings), allocatable, intent(out) :: global_settings + + allocate (global_settings) + global_settings%path_to_folder = tmp_folder + global_settings%file_name = config_file_name + end subroutine + !> Throw error when custom path to config file was entered but no folder exists. + subroutine no_folder(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings call delete_tmp_folder() - call get_global_settings(global_settings, error, join_path(tmp_folder, config_file_name)) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) + end subroutine no_folder + + !> Throw error when custom path to config file was entered but no file exists. + subroutine no_file(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + call delete_tmp_folder() + call mkdir(tmp_folder) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) end subroutine no_file !> Config file exists and working directory is set. subroutine empty_file(error) - type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['']) + call filewrite(join_path(tmp_folder, config_file_name), ['']) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) - if (.not. allocated(global_settings)) then - call test_failed(error, 'global_settings not allocated') - return - end if - - if (.not. allocated(global_settings%path)) then - call test_failed(error, 'global_settings%path not allocated') - return - end if + if (allocated(error)) return if (allocated(global_settings%registry_settings)) then call test_failed(error, 'global_settings%registry_settings should not be allocated') return end if - end subroutine empty_file subroutine empty_registry_table(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['[registry]']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]']) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) + if (allocated(error)) return + if (.not. allocated(global_settings%registry_settings)) then call test_failed(error, 'global_settings%registry_settings not allocated') return @@ -115,38 +124,39 @@ subroutine empty_registry_table(error) call test_failed(error, "Url shouldn't be allocated") return end if - end subroutine subroutine has_non_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['[registry]', 'path="abc"']) - call get_global_settings(global_settings, error, path_to_config_file) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) + + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) end subroutine subroutine has_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['[registry]', 'path="tmp"']) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=10) :: '[registry]', 'path="."']) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) + if (allocated(error)) return + if (.not. allocated(global_settings%registry_settings%path)) then call test_failed(error, 'Path not allocated') return @@ -156,12 +166,13 @@ subroutine has_existent_path_to_registry(error) call test_failed(error, "Url shouldn't be allocated") return end if + end subroutine subroutine absolute_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file, abs_path + character(len=:), allocatable :: abs_path call delete_tmp_folder() call mkdir(tmp_folder) @@ -170,10 +181,11 @@ subroutine absolute_path_to_registry(error) if (allocated(error)) return - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) @@ -193,15 +205,15 @@ subroutine absolute_path_to_registry(error) subroutine relative_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file, abs_path + character(len=:), allocatable :: abs_path call delete_tmp_folder() - call mkdir(tmp_folder) + call mkdir(join_path(tmp_folder, 'abc')) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['[registry]', 'path="tmp"']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -209,7 +221,7 @@ subroutine relative_path_to_registry(error) if (allocated(error)) return - if (global_settings%registry_settings%path /= abs_path) then + if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") return end if @@ -218,15 +230,16 @@ subroutine relative_path_to_registry(error) subroutine canonical_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file, abs_path + character(len=:), allocatable :: abs_path call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, [character(len=80) :: '[registry]', "path='"//join_path('.', 'tmp')//"'"]) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -243,20 +256,20 @@ subroutine canonical_path_to_registry(error) subroutine has_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['[registry]', 'url="http"']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'url="http"']) - call get_global_settings(global_settings, error, path_to_config_file) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) if (allocated(global_settings%registry_settings%path)) then - call test_failed(error, "Path shouldn't be allocated") + call test_failed(error, "Path shouldn't be allocated: '" & + //global_settings%registry_settings%path//"'") return end if @@ -269,14 +282,15 @@ subroutine has_url_to_registry(error) subroutine has_both_path_and_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: path_to_config_file call delete_tmp_folder() call mkdir(tmp_folder) - path_to_config_file = join_path(tmp_folder, config_file_name) - call filewrite(path_to_config_file, ['[registry]', 'path="tmp"', 'url="http"']) - call get_global_settings(global_settings, error, path_to_config_file) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=10) :: '[registry]', 'path="."', 'url="http"']) + + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) end subroutine From 9c9de7f97f2834e7f4d7ca327c24aa9fd1861359 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 6 Jan 2023 20:37:05 +0100 Subject: [PATCH 030/799] Fix test --- src/fpm_filesystem.F90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 745aa0f18e..287b6b9a4d 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -1007,6 +1007,11 @@ logical function is_absolute_path(path, is_unix) if (is_unix_os) then is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' else + if (len(path) < 2) then + is_absolute_path = .false. + return + end if + is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' end if From e955e09dff9ad00a2a07336200dfa5d028596007 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 6 Jan 2023 20:38:10 +0100 Subject: [PATCH 031/799] Potentially shorten --- src/fpm_filesystem.F90 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 287b6b9a4d..9cb881548d 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -1007,12 +1007,7 @@ logical function is_absolute_path(path, is_unix) if (is_unix_os) then is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' else - if (len(path) < 2) then - is_absolute_path = .false. - return - end if - - is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' + is_absolute_path = len(path) > 1 .and. index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' end if end function is_absolute_path From 818d496f33746ab508caabd454bb6015fbd236f6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 6 Jan 2023 20:55:32 +0100 Subject: [PATCH 032/799] Revert and try to identify error --- src/fpm_filesystem.F90 | 7 ++++++- test/fpm_test/test_settings.f90 | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 9cb881548d..287b6b9a4d 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -1007,7 +1007,12 @@ logical function is_absolute_path(path, is_unix) if (is_unix_os) then is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' else - is_absolute_path = len(path) > 1 .and. index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' + if (len(path) < 2) then + is_absolute_path = .false. + return + end if + + is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' end if end function is_absolute_path diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 41555ee5ae..ac73401bc8 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -217,6 +217,9 @@ subroutine relative_path_to_registry(error) call get_absolute_path(tmp_folder, abs_path, error) + print *, abs_path + print *, join_path(abs_path, 'abc') + call os_delete_dir(os_is_unix(), tmp_folder) if (allocated(error)) return From 49d94cbb4d42a809d9ca63a74a8fe6ceaab2d7a6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 6 Jan 2023 21:42:30 +0100 Subject: [PATCH 033/799] Not use canon_path bc it ain't working on Windows --- src/fpm_settings.f90 | 23 ++++++++++++----------- test/fpm_test/test_settings.f90 | 3 --- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 3f1764f000..1159517ffa 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,7 +1,7 @@ !> Manages global settings which are defined in the global config file. module fpm_settings use fpm_command_line, only: fpm_global_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path, canon_path + use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value @@ -90,7 +90,7 @@ subroutine get_registry_settings(global_settings, table, error) type(toml_table), intent(inout) :: table type(error_t), allocatable, intent(out) :: error type(toml_table), pointer :: child - character(:), allocatable :: path, url, abs_path + character(:), allocatable :: path, url integer :: stat call get_value(table, 'registry', child, requested=.false., stat=stat) @@ -112,20 +112,21 @@ subroutine get_registry_settings(global_settings, table, error) call fatal_error(error, 'Error parsing path to registry: "'//path//'"') return end if - if (allocated(path)) then if (is_absolute_path(path)) then - abs_path = path - else - abs_path = canon_path(join_path(global_settings%path_to_folder, path)) - end if + if (.not. exists(path)) then + call fatal_error(error, "No registry at: '"//path//"'") + return + end if - if (.not. exists(abs_path)) then - call fatal_error(error, "No registry at: '"//abs_path//"'") - return + global_settings%registry_settings%path = path + else + ! Get canonical path, which works both on Unix and Windows. + call get_absolute_path(join_path(global_settings%path_to_folder, path), & + global_settings%registry_settings%path, error) + if (allocated(error)) return end if - global_settings%registry_settings%path = abs_path end if call get_value(child, 'url', url, stat=stat) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index ac73401bc8..41555ee5ae 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -217,9 +217,6 @@ subroutine relative_path_to_registry(error) call get_absolute_path(tmp_folder, abs_path, error) - print *, abs_path - print *, join_path(abs_path, 'abc') - call os_delete_dir(os_is_unix(), tmp_folder) if (allocated(error)) return From f707be8a5e994536a73e6b71b85dd558ec95ce89 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 6 Jan 2023 23:29:18 +0100 Subject: [PATCH 034/799] Reformat with 4 spaces of indentation --- src/fpm_filesystem.F90 | 102 ++++---- src/fpm_settings.f90 | 266 ++++++++++---------- test/fpm_test/test_settings.f90 | 426 ++++++++++++++++---------------- 3 files changed, 397 insertions(+), 397 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 287b6b9a4d..4d5aa12691 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -959,62 +959,62 @@ subroutine os_delete_dir(unix, dir, echo) end subroutine os_delete_dir -!> Determine the path prefix to the local folder. Used for installation, registry etc. -function get_local_prefix(os) result(prefix) - !> Installation prefix - character(len=:), allocatable :: prefix - !> Platform identifier - integer, intent(in), optional :: os - - !> Default installation prefix on Unix platforms - character(len=*), parameter :: default_prefix_unix = "/usr/local" - !> Default installation prefix on Windows platforms - character(len=*), parameter :: default_prefix_win = "C:\" - - character(len=:), allocatable :: home - - if (os_is_unix(os)) then - call env_variable(home, "HOME") - if (allocated(home)) then - prefix = join_path(home, ".local") - else - prefix = default_prefix_unix - end if - else - call env_variable(home, "APPDATA") - if (allocated(home)) then - prefix = join_path(home, "local") - else - prefix = default_prefix_win - end if - end if + !> Determine the path prefix to the local folder. Used for installation, registry etc. + function get_local_prefix(os) result(prefix) + !> Installation prefix + character(len=:), allocatable :: prefix + !> Platform identifier + integer, intent(in), optional :: os + + !> Default installation prefix on Unix platforms + character(len=*), parameter :: default_prefix_unix = "/usr/local" + !> Default installation prefix on Windows platforms + character(len=*), parameter :: default_prefix_win = "C:\" + + character(len=:), allocatable :: home + + if (os_is_unix(os)) then + call env_variable(home, "HOME") + if (allocated(home)) then + prefix = join_path(home, ".local") + else + prefix = default_prefix_unix + end if + else + call env_variable(home, "APPDATA") + if (allocated(home)) then + prefix = join_path(home, "local") + else + prefix = default_prefix_win + end if + end if -end function get_local_prefix + end function get_local_prefix - !> Returns .true. if provided path is absolute. - logical function is_absolute_path(path, is_unix) - character(len=*), intent(in) :: path - logical, optional, intent(in):: is_unix - character(len=*), parameter :: letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - logical :: is_unix_os + !> Returns .true. if provided path is absolute. + logical function is_absolute_path(path, is_unix) + character(len=*), intent(in) :: path + logical, optional, intent(in):: is_unix + character(len=*), parameter :: letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + logical :: is_unix_os - if (present(is_unix)) then - is_unix_os = is_unix - else - is_unix_os = os_is_unix() - end if + if (present(is_unix)) then + is_unix_os = is_unix + else + is_unix_os = os_is_unix() + end if - if (is_unix_os) then - is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' - else - if (len(path) < 2) then - is_absolute_path = .false. - return - end if + if (is_unix_os) then + is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' + else + if (len(path) < 2) then + is_absolute_path = .false. + return + end if - is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' - end if + is_absolute_path = index(letters, path(1:1)) /= 0 .and. path(2:2) == ':' + end if - end function is_absolute_path + end function is_absolute_path end module fpm_filesystem diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 1159517ffa..fb7de41ae1 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,151 +1,151 @@ !> Manages global settings which are defined in the global config file. module fpm_settings - use fpm_command_line, only: fpm_global_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path - use fpm_environment, only: os_is_unix - use fpm_error, only: error_t, fatal_error - use fpm_toml, only: toml_table, toml_error, toml_stat, get_value - use fpm_os, only: get_current_directory, change_directory, get_absolute_path - use tomlf, only: toml_load - implicit none - private - public :: get_global_settings + use fpm_command_line, only: fpm_global_settings + use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path + use fpm_environment, only: os_is_unix + use fpm_error, only: error_t, fatal_error + use fpm_toml, only: toml_table, toml_error, toml_stat, get_value + use fpm_os, only: get_current_directory, change_directory, get_absolute_path + use tomlf, only: toml_load + implicit none + private + public :: get_global_settings contains - !> Obtain global settings from the global config file. - subroutine get_global_settings(global_settings, error) - !> Global settings to be obtained. - type(fpm_global_settings), allocatable, intent(inout) :: global_settings - !> Error reading config file. - type(error_t), allocatable, intent(out) :: error - !> Absolute path to the config file. - character(len=:), allocatable :: abs_path_to_config - !> TOML table to be filled with global config settings. - type(toml_table), allocatable :: table - !> Error parsing to TOML table. - type(toml_error), allocatable :: parse_error - - if (.not. allocated(global_settings)) allocate (global_settings) - - ! Use custom path to the config file if it was specified. - if (allocated(global_settings%path_to_folder) .and. allocated(global_settings%file_name)) then - ! Throw error if folder doesn't exist. - if (.not. exists(global_settings%path_to_folder)) then - call fatal_error(error, 'Folder not found: "'//global_settings%path_to_folder//'"') - return - end if + !> Obtain global settings from the global config file. + subroutine get_global_settings(global_settings, error) + !> Global settings to be obtained. + type(fpm_global_settings), allocatable, intent(inout) :: global_settings + !> Error reading config file. + type(error_t), allocatable, intent(out) :: error + !> Absolute path to the config file. + character(len=:), allocatable :: abs_path_to_config + !> TOML table to be filled with global config settings. + type(toml_table), allocatable :: table + !> Error parsing to TOML table. + type(toml_error), allocatable :: parse_error + + if (.not. allocated(global_settings)) allocate (global_settings) + + ! Use custom path to the config file if it was specified. + if (allocated(global_settings%path_to_folder) .and. allocated(global_settings%file_name)) then + ! Throw error if folder doesn't exist. + if (.not. exists(global_settings%path_to_folder)) then + call fatal_error(error, 'Folder not found: "'//global_settings%path_to_folder//'"') + return + end if - ! Throw error if file doesn't exist. - if (.not. exists(global_settings%full_path())) then - call fatal_error(error, 'File not found: "'//global_settings%full_path()//'"') - return - end if - - ! Make sure that the path to the global config file is absolute. - call get_absolute_path(global_settings%path_to_folder, abs_path_to_config, error) - if (allocated(error)) return - - global_settings%path_to_folder = abs_path_to_config - else - ! Use default path if it wasn't specified. - if (os_is_unix()) then - global_settings%path_to_folder = join_path(get_local_prefix(), 'share', 'fpm') - else - global_settings%path_to_folder = join_path(get_local_prefix(), 'fpm') - end if - - ! Use default file name. - global_settings%file_name = 'config.toml' - - ! Deallocate and return if path doesn't exist. - if (.not. exists(global_settings%path_to_folder)) then - deallocate (global_settings%path_to_folder) - deallocate (global_settings%file_name) + ! Throw error if file doesn't exist. + if (.not. exists(global_settings%full_path())) then + call fatal_error(error, 'File not found: "'//global_settings%full_path()//'"') + return + end if + + ! Make sure that the path to the global config file is absolute. + call get_absolute_path(global_settings%path_to_folder, abs_path_to_config, error) + if (allocated(error)) return + + global_settings%path_to_folder = abs_path_to_config + else + ! Use default path if it wasn't specified. + if (os_is_unix()) then + global_settings%path_to_folder = join_path(get_local_prefix(), 'share', 'fpm') + else + global_settings%path_to_folder = join_path(get_local_prefix(), 'fpm') + end if + + ! Use default file name. + global_settings%file_name = 'config.toml' + + ! Deallocate and return if path doesn't exist. + if (.not. exists(global_settings%path_to_folder)) then + deallocate (global_settings%path_to_folder) + deallocate (global_settings%file_name) + return + end if + + ! Deallocate name and return if the config file doesn't exist. + if (.not. exists(global_settings%full_path())) then + deallocate (global_settings%file_name) + return + end if + end if + + ! Load into TOML table. + call toml_load(table, global_settings%full_path(), error=parse_error) + + if (allocated(parse_error)) then + allocate (error) + call move_alloc(parse_error%message, error%message) return - end if + end if + + ! Read registry subtable. + call get_registry_settings(global_settings, table, error) + + end subroutine get_global_settings + + subroutine get_registry_settings(global_settings, table, error) + type(fpm_global_settings), intent(inout) :: global_settings + type(toml_table), intent(inout) :: table + type(error_t), allocatable, intent(out) :: error + type(toml_table), pointer :: child + character(:), allocatable :: path, url + integer :: stat - ! Deallocate name and return if the config file doesn't exist. - if (.not. exists(global_settings%full_path())) then - deallocate (global_settings%file_name) + call get_value(table, 'registry', child, requested=.false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error reading registry from config file "'// & + global_settings%full_path()//'"') return - end if - end if - - ! Load into TOML table. - call toml_load(table, global_settings%full_path(), error=parse_error) - - if (allocated(parse_error)) then - allocate (error) - call move_alloc(parse_error%message, error%message) - return - end if - - ! Read registry subtable. - call get_registry_settings(global_settings, table, error) - - end subroutine get_global_settings - - subroutine get_registry_settings(global_settings, table, error) - type(fpm_global_settings), intent(inout) :: global_settings - type(toml_table), intent(inout) :: table - type(error_t), allocatable, intent(out) :: error - type(toml_table), pointer :: child - character(:), allocatable :: path, url - integer :: stat - - call get_value(table, 'registry', child, requested=.false., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading registry from config file "'// & - global_settings%full_path()//'"') - return - end if - - ! Quietly return if no registry table was found. - if (.not. associated(child)) return - - allocate (global_settings%registry_settings) - - call get_value(child, 'path', path, stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, 'Error parsing path to registry: "'//path//'"') - return - end if - if (allocated(path)) then - if (is_absolute_path(path)) then - if (.not. exists(path)) then - call fatal_error(error, "No registry at: '"//path//"'") - return - end if + end if - global_settings%registry_settings%path = path - else - ! Get canonical path, which works both on Unix and Windows. - call get_absolute_path(join_path(global_settings%path_to_folder, path), & - global_settings%registry_settings%path, error) - if (allocated(error)) return - end if + ! Quietly return if no registry table was found. + if (.not. associated(child)) return - end if + allocate (global_settings%registry_settings) - call get_value(child, 'url', url, stat=stat) + call get_value(child, 'path', path, stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error, 'Error parsing url to registry: "'//url//'"') - return - end if + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error parsing path to registry: "'//path//'"') + return + end if + if (allocated(path)) then + if (is_absolute_path(path)) then + if (.not. exists(path)) then + call fatal_error(error, "No registry at: '"//path//"'") + return + end if + + global_settings%registry_settings%path = path + else + ! Get canonical path, which works both on Unix and Windows. + call get_absolute_path(join_path(global_settings%path_to_folder, path), & + global_settings%registry_settings%path, error) + if (allocated(error)) return + end if + + end if - if (allocated(url)) then - ! Throw error when both path and url were provided. - if (allocated(path)) then - call fatal_error(error, 'Do not provide both path and url to the registry') + call get_value(child, 'url', url, stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error parsing url to registry: "'//url//'"') return - end if + end if + + if (allocated(url)) then + ! Throw error when both path and url were provided. + if (allocated(path)) then + call fatal_error(error, 'Do not provide both path and url to the registry') + return + end if - global_settings%registry_settings%url = url - end if + global_settings%registry_settings%url = url + end if - end subroutine get_registry_settings + end subroutine get_registry_settings end module fpm_settings diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 41555ee5ae..79dbbb6d6a 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -1,297 +1,297 @@ module test_settings - use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_command_line, only: fpm_global_settings - use fpm_settings, only: get_global_settings - use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists - use fpm_environment, only: os_is_unix - use fpm_toml, only: new_table - use fpm_os, only: get_absolute_path + use testsuite, only: new_unittest, unittest_t, error_t, test_failed + use fpm_command_line, only: fpm_global_settings + use fpm_settings, only: get_global_settings + use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists + use fpm_environment, only: os_is_unix + use fpm_toml, only: new_table + use fpm_os, only: get_absolute_path - implicit none - private - public :: collect_settings + implicit none + private + public :: collect_settings - character(len=*), parameter :: tmp_folder = 'tmp' - character(len=*), parameter :: config_file_name = 'config.toml' + character(len=*), parameter :: tmp_folder = 'tmp' + character(len=*), parameter :: config_file_name = 'config.toml' contains - !> Collect unit tests. - subroutine collect_settings(tests) + !> Collect unit tests. + subroutine collect_settings(tests) - !> Unit tests to collect. - type(unittest_t), allocatable, intent(out) :: tests(:) + !> Unit tests to collect. + type(unittest_t), allocatable, intent(out) :: tests(:) - tests = [ & - & new_unittest('no-folder', no_folder, should_fail=.true.), & - & new_unittest('no-file', no_file, should_fail=.true.), & - & new_unittest('empty-file', empty_file), & - & new_unittest('empty-registry-table', empty_registry_table), & - & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & - & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & - & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & - & new_unittest('relative-path-to-registry', relative_path_to_registry), & - & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & - & new_unittest('has-url-to-registry', has_url_to_registry), & - & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.) & - ] + tests = [ & + & new_unittest('no-folder', no_folder, should_fail=.true.), & + & new_unittest('no-file', no_file, should_fail=.true.), & + & new_unittest('empty-file', empty_file), & + & new_unittest('empty-registry-table', empty_registry_table), & + & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & + & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & + & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & + & new_unittest('relative-path-to-registry', relative_path_to_registry), & + & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & + & new_unittest('has-url-to-registry', has_url_to_registry), & + & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.) & + ] - end subroutine collect_settings + end subroutine collect_settings - subroutine delete_tmp_folder - if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - end + subroutine delete_tmp_folder + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + end - subroutine setup_global_settings(global_settings) - type(fpm_global_settings), allocatable, intent(out) :: global_settings + subroutine setup_global_settings(global_settings) + type(fpm_global_settings), allocatable, intent(out) :: global_settings - allocate (global_settings) - global_settings%path_to_folder = tmp_folder - global_settings%file_name = config_file_name - end subroutine + allocate (global_settings) + global_settings%path_to_folder = tmp_folder + global_settings%file_name = config_file_name + end subroutine - !> Throw error when custom path to config file was entered but no folder exists. - subroutine no_folder(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + !> Throw error when custom path to config file was entered but no folder exists. + subroutine no_folder(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) - end subroutine no_folder + call delete_tmp_folder() + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) + end subroutine no_folder - !> Throw error when custom path to config file was entered but no file exists. - subroutine no_file(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + !> Throw error when custom path to config file was entered but no file exists. + subroutine no_file(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) - end subroutine no_file + call delete_tmp_folder() + call mkdir(tmp_folder) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) + end subroutine no_file - !> Config file exists and working directory is set. - subroutine empty_file(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + !> Config file exists and working directory is set. + subroutine empty_file(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['']) + call filewrite(join_path(tmp_folder, config_file_name), ['']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(error)) return + if (allocated(error)) return - if (allocated(global_settings%registry_settings)) then - call test_failed(error, 'global_settings%registry_settings should not be allocated') - return - end if - end subroutine empty_file + if (allocated(global_settings%registry_settings)) then + call test_failed(error, 'global_settings%registry_settings should not be allocated') + return + end if + end subroutine empty_file - subroutine empty_registry_table(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + subroutine empty_registry_table(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'global_settings%registry_settings not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'global_settings%registry_settings not allocated') + return + end if - if (allocated(global_settings%registry_settings%path)) then - call test_failed(error, "Path shouldn't be allocated") - return - end if + if (allocated(global_settings%registry_settings%path)) then + call test_failed(error, "Path shouldn't be allocated") + return + end if - if (allocated(global_settings%registry_settings%url)) then - call test_failed(error, "Url shouldn't be allocated") - return - end if - end subroutine + if (allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url shouldn't be allocated") + return + end if + end subroutine - subroutine has_non_existent_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + subroutine has_non_existent_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) - end subroutine + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine - subroutine has_existent_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + subroutine has_existent_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=10) :: '[registry]', 'path="."']) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=10) :: '[registry]', 'path="."']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings%path)) then - call test_failed(error, 'Path not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if - if (allocated(global_settings%registry_settings%url)) then - call test_failed(error, "Url shouldn't be allocated") - return - end if + if (allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url shouldn't be allocated") + return + end if - end subroutine + end subroutine - subroutine absolute_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: abs_path + subroutine absolute_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: abs_path - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call get_absolute_path(tmp_folder, abs_path, error) + call get_absolute_path(tmp_folder, abs_path, error) - if (allocated(error)) return + if (allocated(error)) return - call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings%path)) then - call test_failed(error, 'Path not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if - if (global_settings%registry_settings%path /= abs_path) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - subroutine relative_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: abs_path + subroutine relative_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: abs_path - call delete_tmp_folder() - call mkdir(join_path(tmp_folder, 'abc')) + call delete_tmp_folder() + call mkdir(join_path(tmp_folder, 'abc')) - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call get_absolute_path(tmp_folder, abs_path, error) + call get_absolute_path(tmp_folder, abs_path, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(error)) return + if (allocated(error)) return - if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - subroutine canonical_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings - character(len=:), allocatable :: abs_path + subroutine canonical_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings + character(len=:), allocatable :: abs_path - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call get_absolute_path(tmp_folder, abs_path, error) + call get_absolute_path(tmp_folder, abs_path, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(error)) return + if (allocated(error)) return - if (global_settings%registry_settings%path /= abs_path) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - subroutine has_url_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + subroutine has_url_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'url="http"']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'url="http"']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call os_delete_dir(os_is_unix(), tmp_folder) - if (allocated(global_settings%registry_settings%path)) then - call test_failed(error, "Path shouldn't be allocated: '" & - //global_settings%registry_settings%path//"'") - return - end if + if (allocated(global_settings%registry_settings%path)) then + call test_failed(error, "Path shouldn't be allocated: '" & + //global_settings%registry_settings%path//"'") + return + end if - if (.not. allocated(global_settings%registry_settings%url)) then - call test_failed(error, "Url not allocated") - return - end if - end subroutine + if (.not. allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url not allocated") + return + end if + end subroutine - subroutine has_both_path_and_url_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + subroutine has_both_path_and_url_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings), allocatable :: global_settings - call delete_tmp_folder() - call mkdir(tmp_folder) + call delete_tmp_folder() + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=10) :: '[registry]', 'path="."', 'url="http"']) + call filewrite(join_path(tmp_folder, config_file_name), & + [character(len=10) :: '[registry]', 'path="."', 'url="http"']) - call setup_global_settings(global_settings) - call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) - end subroutine + call setup_global_settings(global_settings) + call get_global_settings(global_settings, error) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine end module test_settings From a6ebb91b1e0ae52cae1038dc9726e1f466595d7a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 7 Jan 2023 15:31:54 +0100 Subject: [PATCH 035/799] Treat ~ as relative and adjust tests --- src/fpm_filesystem.F90 | 4 +- test/fpm_test/test_filesystem.f90 | 85 +++++++++++++++++-------------- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 4d5aa12691..b28c582123 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -992,6 +992,8 @@ function get_local_prefix(os) result(prefix) end function get_local_prefix !> Returns .true. if provided path is absolute. + !> + !> `~` not treated as absolute. logical function is_absolute_path(path, is_unix) character(len=*), intent(in) :: path logical, optional, intent(in):: is_unix @@ -1005,7 +1007,7 @@ logical function is_absolute_path(path, is_unix) end if if (is_unix_os) then - is_absolute_path = path(1:1) == '/' .or. path(1:1) == '~' + is_absolute_path = path(1:1) == '/' else if (len(path) < 2) then is_absolute_path = .false. diff --git a/test/fpm_test/test_filesystem.f90 b/test/fpm_test/test_filesystem.f90 index ed3f17e5be..4eb80b82cd 100644 --- a/test/fpm_test/test_filesystem.f90 +++ b/test/fpm_test/test_filesystem.f90 @@ -1,5 +1,5 @@ module test_filesystem - use testsuite, only : new_unittest, unittest_t, error_t, test_failed + use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: canon_path, is_dir, mkdir, os_delete_dir, & join_path, is_absolute_path use fpm_environment, only: OS_WINDOWS, get_os_type, os_is_unix @@ -10,7 +10,6 @@ module test_filesystem contains - !> Collect all exported unit tests subroutine collect_filesystem(tests) @@ -25,7 +24,6 @@ subroutine collect_filesystem(tests) end subroutine collect_filesystem - subroutine test_canon_path(error) !> Error handling @@ -85,7 +83,6 @@ subroutine test_canon_path(error) end subroutine test_canon_path - !> Check a character variable against a reference value subroutine check_string(error, actual, expected) @@ -100,19 +97,18 @@ subroutine check_string(error, actual, expected) if (actual /= expected) then call test_failed(error, & - "Character value mismatch "//& - "expected '"//expected//"' but got '"//actual//"'") + "Character value mismatch "// & + "expected '"//expected//"' but got '"//actual//"'") end if end subroutine check_string - subroutine test_mkdir_rmdir(error) !> Error handling type(error_t), allocatable, intent(out) :: error - call check_mkdir(error, join_path("tmpdir","subdir")) + call check_mkdir(error, join_path("tmpdir", "subdir")) if (allocated(error)) return call check_rmdir(error, "tmpdir") @@ -120,7 +116,6 @@ subroutine test_mkdir_rmdir(error) end subroutine test_mkdir_rmdir - !> Create a directory and verify its existence subroutine check_mkdir(error, path) @@ -133,7 +128,7 @@ subroutine check_mkdir(error, path) ! Directory shouldn't exist before it's created if (is_dir(path)) then call test_failed(error, & - "Directory path "//path//" already exists before its creation") + "Directory path "//path//" already exists before its creation") return end if @@ -141,13 +136,12 @@ subroutine check_mkdir(error, path) call mkdir(path) ! Check that directory is indeed created - if (.not.is_dir(path)) then + if (.not. is_dir(path)) then call test_failed(error, & - "Directory path "//path//" cannot be created") + "Directory path "//path//" cannot be created") end if - end subroutine check_mkdir - + end subroutine check_mkdir !> Create a directory and verify its existence subroutine check_rmdir(error, path) @@ -161,17 +155,17 @@ subroutine check_rmdir(error, path) ! Directory should exist before it's deleted if (.not. is_dir(path)) then call test_failed(error, & - "Directory path "//path//" doesn't exist before its deletion") + "Directory path "//path//" doesn't exist before its deletion") return end if ! Delete directory - call os_delete_dir(os_is_unix(),path) + call os_delete_dir(os_is_unix(), path) ! Check that directory is indeed deleted if (is_dir(path)) then call test_failed(error, & - "Directory path "//path//" cannot be deleted") + "Directory path "//path//" cannot be deleted") end if end subroutine check_rmdir @@ -179,48 +173,60 @@ end subroutine check_rmdir subroutine test_is_absolute_path(error) type(error_t), allocatable, intent(out) :: error + ! Unix tests if (is_absolute_path('.', is_unix=.true.)) then - call test_failed(error, "Relative path '.' isn't absolute") + call test_failed(error, "Path '.' isn't absolute") return end if if (is_absolute_path('abc', is_unix=.true.)) then - call test_failed(error, "Relative path 'abc' isn't absolute") - return + call test_failed(error, "Path 'abc' isn't absolute") + return end if - if (.not. is_absolute_path('/', is_unix=.true.)) then - call test_failed(error, "Path '/' is absolute") + if (is_absolute_path('~a', is_unix=.true.)) then + call test_failed(error, "Path '~a' isn't absolute") + return + end if + + if (is_absolute_path('C:', is_unix=.true.)) then + call test_failed(error, "Path 'C:' isn't absolute on Unix") + return + end if + + if (is_absolute_path('~', is_unix=.true.)) then + call test_failed(error, "Path '~' isn't absolute") return end if - if (.not. is_absolute_path('/abc', is_unix=.true.)) then - call test_failed(error, "Path '/abc' is absolute") + if (is_absolute_path('~/', is_unix=.true.)) then + call test_failed(error, "Path '~/' isn't absolute") return end if - if (.not. is_absolute_path('~/', is_unix=.true.)) then - call test_failed(error, "Path '~/' is absolute") + if (.not. is_absolute_path('/', is_unix=.true.)) then + call test_failed(error, "Path '/' is absolute") return end if - if (.not. is_absolute_path('~/', is_unix=.true.)) then - call test_failed(error, "Path '~/' is absolute") + if (.not. is_absolute_path('/a', is_unix=.true.)) then + call test_failed(error, "Path '/a' is absolute") return end if + ! Windows tests if (is_absolute_path('abc', is_unix=.false.)) then - call test_failed(error, "Relative path 'abc' isn't absolute") + call test_failed(error, "Path 'abc' isn't absolute") return end if if (is_absolute_path('..', is_unix=.false.)) then - call test_failed(error, "Relative path '..' isn't absolute") + call test_failed(error, "Path '..' isn't absolute") return end if - if (is_absolute_path('abc', is_unix=.false.)) then - call test_failed(error, "Relative path 'abc' isn't absolute") + if (is_absolute_path('~', is_unix=.false.)) then + call test_failed(error, "Path '~' isn't absolute") return end if @@ -234,6 +240,16 @@ subroutine test_is_absolute_path(error) return end if + if (is_absolute_path('1:', is_unix=.false.)) then + call test_failed(error, "Path '1:' isn't absolute") + return + end if + + if (is_absolute_path('C', is_unix=.false.)) then + call test_failed(error, "Path 'C' isn't absolute") + return + end if + if (.not. is_absolute_path('C:', is_unix=.false.)) then call test_failed(error, "Path 'C:' is absolute") return @@ -249,11 +265,6 @@ subroutine test_is_absolute_path(error) return end if - if (is_absolute_path('1:', is_unix=.false.)) then - call test_failed(error, "Path '1:' isn't absolute") - return - end if - end subroutine test_is_absolute_path end module test_filesystem From 368939274a4491359f2e5326cd44498d43ddd0fd Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 7 Jan 2023 15:58:50 +0100 Subject: [PATCH 036/799] Move fpm_global_settings and fpm_registry_settings to fpm_settings module --- app/main.f90 | 3 +-- src/fpm_command_line.f90 | 33 ------------------------------- src/fpm_settings.f90 | 35 +++++++++++++++++++++++++++++++-- test/fpm_test/test_settings.f90 | 3 +-- 4 files changed, 35 insertions(+), 39 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index cc1507d7ac..af8d6ed898 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -2,7 +2,6 @@ program main use, intrinsic :: iso_fortran_env, only : error_unit, output_unit use fpm_command_line, only: & fpm_cmd_settings, & - fpm_global_settings, & fpm_new_settings, & fpm_build_settings, & fpm_run_settings, & @@ -18,7 +17,7 @@ program main use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update use fpm_os, only: change_directory, get_current_directory -use fpm_settings, only: get_global_settings +use fpm_settings, only: fpm_global_settings, get_global_settings implicit none diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 96ba35e5e7..7ca7fbc5a6 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -41,7 +41,6 @@ module fpm_command_line private public :: fpm_cmd_settings, & - fpm_global_settings, & fpm_build_settings, & fpm_install_settings, & fpm_new_settings, & @@ -58,23 +57,6 @@ module fpm_command_line integer,parameter :: ibug=4096 -type, extends(fpm_cmd_settings) :: fpm_global_settings - !> Path to the global config file excluding the file name. - character(len=:), allocatable :: path_to_folder - !> Name of the global config file. The default is `config.toml`. - character(len=:), allocatable :: file_name - type(fpm_registry_settings), allocatable :: registry_settings -contains - procedure :: full_path -end type - -type, extends(fpm_cmd_settings) :: fpm_registry_settings - character(len=:), allocatable :: path - character(len=:), allocatable :: url -contains - procedure :: uses_default_registry -end type - type, extends(fpm_cmd_settings) :: fpm_new_settings character(len=:),allocatable :: name logical :: with_executable=.false. @@ -1341,19 +1323,4 @@ function get_fpm_env(env, default) result(val) val = get_env(fpm_prefix//env, default) end function get_fpm_env - !> The full path to the global config file. - function full_path(self) result(result) - class(fpm_global_settings), intent(in) :: self - character(len=:), allocatable :: result - - result = join_path(self%path_to_folder, self%file_name) - end function - - !> The official registry is used by default when no local or custom registry was specified. - pure logical function uses_default_registry(self) - class(fpm_registry_settings), intent(in) :: self - - uses_default_registry = .not. allocated(self%path) .and. .not. allocated(self%url) - end function - end module fpm_command_line diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index fb7de41ae1..a8d5b80509 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,6 +1,5 @@ !> Manages global settings which are defined in the global config file. module fpm_settings - use fpm_command_line, only: fpm_global_settings use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error @@ -9,7 +8,24 @@ module fpm_settings use tomlf, only: toml_load implicit none private - public :: get_global_settings + public :: fpm_global_settings, get_global_settings + + type :: fpm_global_settings + !> Path to the global config file excluding the file name. + character(len=:), allocatable :: path_to_folder + !> Name of the global config file. The default is `config.toml`. + character(len=:), allocatable :: file_name + type(fpm_registry_settings), allocatable :: registry_settings + contains + procedure :: full_path + end type + + type :: fpm_registry_settings + character(len=:), allocatable :: path + character(len=:), allocatable :: url + contains + procedure :: uses_default_registry + end type contains !> Obtain global settings from the global config file. @@ -148,4 +164,19 @@ subroutine get_registry_settings(global_settings, table, error) end subroutine get_registry_settings + !> The full path to the global config file. + function full_path(self) result(result) + class(fpm_global_settings), intent(in) :: self + character(len=:), allocatable :: result + + result = join_path(self%path_to_folder, self%file_name) + end function + + !> The official registry is used by default when no local or custom registry was specified. + pure logical function uses_default_registry(self) + class(fpm_registry_settings), intent(in) :: self + + uses_default_registry = .not. allocated(self%path) .and. .not. allocated(self%url) + end function + end module fpm_settings diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 79dbbb6d6a..f8333f3b21 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -1,7 +1,6 @@ module test_settings use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_command_line, only: fpm_global_settings - use fpm_settings, only: get_global_settings + use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix use fpm_toml, only: new_table From effb7a0ea7afd6fd0003b218ac26a492125b8495 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 11 Jan 2023 16:08:03 +0100 Subject: [PATCH 037/799] Revert resolution of home --- src/fpm_filesystem.F90 | 24 ++++- src/fpm_os.F90 | 164 ++++++++++++++++++++++------ src/fpm_settings.f90 | 23 ++-- test/fpm_test/main.f90 | 5 +- test/fpm_test/test_filesystem.f90 | 27 ++++- test/fpm_test/test_os.f90 | 171 ++++++++++++++++++++++++++++++ test/fpm_test/test_settings.f90 | 22 +++- 7 files changed, 383 insertions(+), 53 deletions(-) create mode 100644 test/fpm_test/test_os.f90 diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index b28c582123..2d22593ebf 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -8,13 +8,13 @@ module fpm_filesystem use fpm_environment, only: separator, get_env, os_is_unix use fpm_strings, only: f_string, replace, string_t, split, notabs, str_begins_with_str use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer - use fpm_error, only : fpm_stop + use fpm_error, only : fpm_stop, error_t, fatal_error implicit none private public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path + LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -1019,4 +1019,24 @@ logical function is_absolute_path(path, is_unix) end function is_absolute_path + !> Get the HOME directory on Unix and the %USERPROFILE% directory on Windows. + subroutine get_home(home, error) + character(len=:), allocatable, intent(out) :: home + type(error_t), allocatable, intent(out) :: error + + if (os_is_unix()) then + call env_variable(home, 'HOME') + if (.not. allocated(home)) then + call fatal_error(error, "Couldn't retrieve 'HOME' variable") + return + end if + else + call env_variable(home, 'USERPROFILE') + if (.not. allocated(home)) then + call fatal_error(error, "Couldn't retrieve '%USERPROFILE%' variable") + return + end if + end if + end subroutine get_home + end module fpm_filesystem diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 3a4ac7cbb7..5b67019e84 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -1,10 +1,14 @@ module fpm_os - use, intrinsic :: iso_c_binding, only : c_char, c_int, c_null_char, c_ptr, c_associated - use fpm_error, only : error_t, fatal_error + use, intrinsic :: iso_c_binding, only: c_char, c_int, c_null_char, c_ptr, c_associated + use fpm_filesystem, only: exists, join_path, get_home + use fpm_environment, only: os_is_unix + use fpm_error, only: error_t, fatal_error implicit none private public :: change_directory, get_current_directory, get_absolute_path + integer(c_int), parameter :: buffersize = 1000_c_int + #ifndef _WIN32 character(len=*), parameter :: pwd_env = "PWD" #else @@ -12,28 +16,47 @@ module fpm_os #endif interface - function chdir(path) result(stat) & + function chdir_(path) result(stat) & #ifndef _WIN32 - bind(C, name="chdir") + bind(C, name="chdir") #else - bind(C, name="_chdir") + bind(C, name="_chdir") #endif import :: c_char, c_int character(kind=c_char, len=1), intent(in) :: path(*) integer(c_int) :: stat - end function chdir + end function chdir_ - function getcwd(buf, bufsize) result(path) & + function getcwd_(buf, bufsize) result(path) & #ifndef _WIN32 - bind(C, name="getcwd") + bind(C, name="getcwd") #else - bind(C, name="_getcwd") + bind(C, name="_getcwd") #endif import :: c_char, c_int, c_ptr character(kind=c_char, len=1), intent(in) :: buf(*) integer(c_int), value, intent(in) :: bufsize type(c_ptr) :: path - end function getcwd + end function getcwd_ + + !> Unix only. For Windows, use `fullpath`. + function realpath(path, resolved_path) result(ptr) & + bind(C, name="_realpath") + import :: c_ptr, c_char + character(kind=c_char, len=1), intent(in) :: path(*) + character(kind=c_char, len=1), intent(out) :: resolved_path(*) + type(c_ptr) :: ptr + end function realpath + + !> Windows only, use `realpath` on Unix. + function fullpath(resolved_path, path, maxLength) result(ptr) & + bind(C, name="_fullpath") + import :: c_ptr, c_char, c_int + character(kind=c_char, len=1), intent(out) :: resolved_path(*) + character(kind=c_char, len=1), intent(in) :: path(*) + integer(c_int), value, intent(in) :: maxLength + type(c_ptr) :: ptr + end function fullpath end interface contains @@ -45,10 +68,10 @@ subroutine change_directory(path, error) character(kind=c_char, len=1), allocatable :: cpath(:) integer :: stat - allocate(cpath(len(path)+1)) - call f_c_character(path, cpath, len(path)+1) + allocate (cpath(len(path) + 1)) + call f_c_character(path, cpath, len(path) + 1) - stat = chdir(cpath) + stat = chdir_(cpath) if (stat /= 0) then call fatal_error(error, "Failed to change directory to '"//path//"'") @@ -60,12 +83,11 @@ subroutine get_current_directory(path, error) type(error_t), allocatable, intent(out) :: error character(kind=c_char, len=1), allocatable :: cpath(:) - integer(c_int), parameter :: buffersize = 1000_c_int type(c_ptr) :: tmp - allocate(cpath(buffersize)) + allocate (cpath(buffersize)) - tmp = getcwd(cpath, buffersize) + tmp = getcwd_(cpath, buffersize) if (c_associated(tmp)) then call c_f_character(cpath, path) else @@ -79,10 +101,10 @@ subroutine f_c_character(rhs, lhs, len) character(len=*), intent(in) :: rhs integer, intent(in) :: len integer :: length - length = min(len-1, len_trim(rhs)) + length = min(len - 1, len_trim(rhs)) lhs(1:length) = transfer(rhs(1:length), lhs(1:length)) - lhs(length+1:length+1) = c_null_char + lhs(length + 1:length + 1) = c_null_char end subroutine f_c_character @@ -97,26 +119,98 @@ subroutine c_f_character(rhs, lhs) exit end if end do - allocate(character(len=ii-1) :: lhs) - lhs = transfer(rhs(1:ii-1), lhs) + + allocate (character(len=ii - 1) :: lhs) + lhs = transfer(rhs(1:ii - 1), lhs) end subroutine c_f_character - !> Determine the absolute from the relative path. - subroutine get_absolute_path(rel_path, abs_path, error) - character(len=*), intent(in) :: rel_path - character(len=:), allocatable, intent(out) :: abs_path + !> Determine the canonical, absolute path for the given path. + subroutine get_realpath(path, real_path, error) + character(len=*), intent(in) :: path + character(len=:), allocatable, intent(out) :: real_path + type(error_t), allocatable, intent(out) :: error + + character(kind=c_char, len=1), allocatable :: appended_path(:) + character(kind=c_char, len=1), allocatable :: cpath(:) + type(c_ptr) :: ptr + + if (.not. exists(path)) then + call fatal_error(error, "Path '"//path//"' does not exist") + return + end if + + allocate (appended_path(len(path) + 1)) + call f_c_character(path, appended_path, len(path) + 1) + + allocate (cpath(buffersize)) + + if (os_is_unix()) then + ptr = realpath(appended_path, cpath) + else + ptr = fullpath(cpath, appended_path, buffersize) + end if + + if (c_associated(ptr)) then + call c_f_character(cpath, real_path) + else + call fatal_error(error, "Failed to retrieve real path for '"//path//"'") + end if + + end subroutine get_realpath + + !> Determine the canonical, absolute path for the given path. + !> It contains expansion of the home folder (~). + subroutine get_absolute_path(path, absolute_path, error) + character(len=*), intent(in) :: path + character(len=:), allocatable, intent(out) :: absolute_path type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: start_dir - - call get_current_directory(start_dir, error) - if (allocated(error)) return - call change_directory(rel_path, error) - if (allocated(error)) return - call get_current_directory(abs_path, error) - if (allocated(error)) return - call change_directory(start_dir, error) - if (allocated(error)) return - end subroutine get_absolute_path + + character(len=:), allocatable :: home + + if (len_trim(path) < 1) then + ! Empty path + call fatal_error(error, 'Path cannot be empty') + return + else if (path(1:1) == '~') then + ! Expand home + call get_home(home, error) + if (allocated(error)) return + + if (len_trim(path) == 1) then + absolute_path = home + return + end if + + if (os_is_unix()) then + if (path(2:2) /= '/') then + call fatal_error(error, "Wrong separator in path: '"//path//"'") + return + end if + else + if (path(2:2) /= '\') then + call fatal_error(error, "Wrong separator in path: '"//path//"'") + return + end if + end if + + if (len_trim(path) == 2) then + absolute_path = home + return + end if + + absolute_path = join_path(home, path(3:len_trim(path))) + + if (.not. exists(absolute_path)) then + call fatal_error(error, "Path not found: '"//absolute_path//"'") + deallocate (absolute_path) + return + end if + else + ! Get canonicalized absolute path from either the absolute or the relative path. + call get_realpath(path, absolute_path, error) + end if + + end subroutine end module fpm_os diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index a8d5b80509..f828a1156f 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,6 +1,6 @@ !> Manages global settings which are defined in the global config file. module fpm_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path + use fpm_filesystem, only: exists, join_path, get_local_prefix use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value @@ -128,19 +128,18 @@ subroutine get_registry_settings(global_settings, table, error) call fatal_error(error, 'Error parsing path to registry: "'//path//'"') return end if + if (allocated(path)) then - if (is_absolute_path(path)) then - if (.not. exists(path)) then - call fatal_error(error, "No registry at: '"//path//"'") - return - end if + ! Relative path, join path to the global config file with the path to the registry. + call get_absolute_path(join_path(global_settings%path_to_folder, path), & + global_settings%registry_settings%path, error) + if (allocated(error)) return - global_settings%registry_settings%path = path - else - ! Get canonical path, which works both on Unix and Windows. - call get_absolute_path(join_path(global_settings%path_to_folder, path), & - global_settings%registry_settings%path, error) - if (allocated(error)) return + ! Check if the new path to the registry exists. + if (.not. exists(global_settings%registry_settings%path)) then + call fatal_error(error, "No registry at: '"//global_settings%registry_settings%path//"'") + deallocate (global_settings%registry_settings%path) + return end if end if diff --git a/test/fpm_test/main.f90 b/test/fpm_test/main.f90 index b2812964f2..c8ee58df9c 100644 --- a/test/fpm_test/main.f90 +++ b/test/fpm_test/main.f90 @@ -13,6 +13,8 @@ program fpm_testing use test_installer, only : collect_installer use test_versioning, only : collect_versioning use test_settings, only : collect_settings + use test_os, only: collect_os + implicit none integer :: stat, is character(len=:), allocatable :: suite_name, test_name @@ -31,7 +33,8 @@ program fpm_testing & new_testsuite("fpm_test_backend", collect_backend), & & new_testsuite("fpm_installer", collect_installer), & & new_testsuite("fpm_versioning", collect_versioning), & - & new_testsuite("fpm_settings", collect_settings) & + ! & new_testsuite("fpm_settings", collect_settings), & + & new_testsuite("fpm_os", collect_os) & & ] call get_argument(1, suite_name) diff --git a/test/fpm_test/test_filesystem.f90 b/test/fpm_test/test_filesystem.f90 index 4eb80b82cd..ad6e86d853 100644 --- a/test/fpm_test/test_filesystem.f90 +++ b/test/fpm_test/test_filesystem.f90 @@ -1,7 +1,7 @@ module test_filesystem use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: canon_path, is_dir, mkdir, os_delete_dir, & - join_path, is_absolute_path + join_path, is_absolute_path, get_home use fpm_environment, only: OS_WINDOWS, get_os_type, os_is_unix implicit none private @@ -19,7 +19,8 @@ subroutine collect_filesystem(tests) tests = [ & & new_unittest("canon-path", test_canon_path), & & new_unittest("create-delete-directory", test_mkdir_rmdir), & - & new_unittest("test-is-absolute-path", test_is_absolute_path) & + & new_unittest("test-is-absolute-path", test_is_absolute_path), & + & new_unittest("test-get-home", test_get_home) & ] end subroutine collect_filesystem @@ -267,4 +268,26 @@ subroutine test_is_absolute_path(error) end subroutine test_is_absolute_path + subroutine test_get_home(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: home + character(len=*), parameter :: letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + + call get_home(home, error) + if (allocated(error)) return + + if (os_is_unix()) then + if (home(1:1) /= '/') then + call test_failed(error, "This doesn't seem to be the correct home path: '"//home//"'") + return + end if + else + if (index(letters, home(1:1)) == 0 .or. home(2:2) /= ':') then + call test_failed(error, "This doesn't seem to be the correct home path: '"//home//"'") + return + end if + end if + + end subroutine test_get_home + end module test_filesystem diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 new file mode 100644 index 0000000000..c69c71f0e7 --- /dev/null +++ b/test/fpm_test/test_os.f90 @@ -0,0 +1,171 @@ +module test_os + use testsuite, only: new_unittest, unittest_t, error_t, test_failed + use fpm_filesystem, only: env_variable, join_path, mkdir, os_delete_dir, is_dir, get_local_prefix, get_home + use fpm_environment, only: os_is_unix + use fpm_os, only: get_absolute_path + + implicit none + private + public :: collect_os + + character(len=*), parameter :: tmp_folder = 'tmp' + +contains + + !> Collect unit tests. + subroutine collect_os(tests) + + !> Unit tests to collect. + type(unittest_t), allocatable, intent(out) :: tests(:) + + tests = [ & + & new_unittest('empty-path', empty_path, should_fail=.true.), & + & new_unittest('only-tilde', only_tilde), & + & new_unittest('invalid-tilde-path', invalid_tilde_path, should_fail=.true.), & + & new_unittest('tilde-correct-separator', tilde_correct_separator), & + & new_unittest('tilde-wrong-separator', tilde_wrong_separator, should_fail=.true.), & + & new_unittest('tilde-nonexistent-path', tilde_nonexistent_path, should_fail=.true.), & + & new_unittest('abs-path-nonexisting', abs_path_nonexisting, should_fail=.true.), & + & new_unittest('abs-path-root', abs_path_root), & + & new_unittest('abs-path-home', abs_path_home) & + ] + + end subroutine collect_os + + subroutine delete_tmp_folder + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + end + + subroutine empty_path(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + call get_absolute_path('', result, error) + end + + subroutine only_tilde(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + character(len=:), allocatable :: home + + call get_absolute_path('~', result, error) + + if (allocated(error)) then + call test_failed(error, "Unexpected error resolving '~'") + return + end if + + if (.not. allocated(result)) then + call test_failed(error, "Unexpected null result resolving '~'") + return + end if + + call get_home(home, error) + if (allocated(error)) return + + if (result /= home) then + call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'") + return + end if + + end subroutine + + subroutine invalid_tilde_path(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + call get_absolute_path('~a', result, error) + end + + subroutine tilde_correct_separator(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + character(len=:), allocatable :: separator + character(len=:), allocatable :: home + + if (os_is_unix()) then + separator = '/' + else + separator = '\' + end if + + call get_absolute_path('~'//separator, result, error) + + call get_home(home, error) + if (allocated(error)) return + + if (result /= home) then + call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'") + return + end if + end + + subroutine tilde_wrong_separator(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + character(len=:), allocatable :: separator + + if (os_is_unix()) then + separator = '\' + else + separator = '/' + end if + + call get_absolute_path('~'//separator, result, error) + end + + !> Entering a non-existing path with ~ should fail. + subroutine tilde_nonexistent_path(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + call get_absolute_path('~/abcde', result, error) + end + + !> Entering a non-existing absolute path should fail. + subroutine abs_path_nonexisting(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + call get_absolute_path('/abcde', result, error) + end + + !> Testing the most obvious absolute path: The root directory. + subroutine abs_path_root(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + character(len=:), allocatable :: home_drive + + if (os_is_unix()) then + call get_absolute_path('/', result, error) + + if (result /= '/') then + call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'") + return + end if + else + call env_variable(home_drive, 'HOMEDRIVE') + call get_absolute_path(home_drive, result, error) + + if (result /= home_drive) then + call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_drive//"'") + return + end if + end if + end + + !> Testing an absolute path which is not root. It should not be altered. + subroutine abs_path_home(error) + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: result + character(len=:), allocatable :: home + + call get_home(home, error) + if (allocated(error)) return + + call get_absolute_path(home, result, error) + if (allocated(error)) return + + if (result /= home) then + call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'") + return + end if + end + +end module test_os diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index f8333f3b21..3c60470768 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -190,6 +190,11 @@ subroutine absolute_path_to_registry(error) if (allocated(error)) return + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if + if (.not. allocated(global_settings%registry_settings%path)) then call test_failed(error, 'Path not allocated') return @@ -220,6 +225,11 @@ subroutine relative_path_to_registry(error) if (allocated(error)) return + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if + if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") return @@ -246,6 +256,11 @@ subroutine canonical_path_to_registry(error) if (allocated(error)) return + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if + if (global_settings%registry_settings%path /= abs_path) then call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") return @@ -266,6 +281,11 @@ subroutine has_url_to_registry(error) call os_delete_dir(os_is_unix(), tmp_folder) + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if + if (allocated(global_settings%registry_settings%path)) then call test_failed(error, "Path shouldn't be allocated: '" & //global_settings%registry_settings%path//"'") @@ -273,7 +293,7 @@ subroutine has_url_to_registry(error) end if if (.not. allocated(global_settings%registry_settings%url)) then - call test_failed(error, "Url not allocated") + call test_failed(error, 'Url not allocated') return end if end subroutine From 837cdcd8b1fc89f78dd9c1c6e71718c6046543fc Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 18 Jan 2023 16:59:49 +0100 Subject: [PATCH 038/799] Use preprocessor in code --- src/fpm_os.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 5b67019e84..7869752245 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -41,7 +41,7 @@ end function getcwd_ !> Unix only. For Windows, use `fullpath`. function realpath(path, resolved_path) result(ptr) & - bind(C, name="_realpath") + bind(C, name="realpath") import :: c_ptr, c_char character(kind=c_char, len=1), intent(in) :: path(*) character(kind=c_char, len=1), intent(out) :: resolved_path(*) @@ -145,11 +145,11 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) - if (os_is_unix()) then - ptr = realpath(appended_path, cpath) - else - ptr = fullpath(cpath, appended_path, buffersize) - end if +#ifndef _WIN32 + ptr = realpath(appended_path, cpath) +#else + ptr = fullpath(cpath, appended_path, buffersize) +#endif if (c_associated(ptr)) then call c_f_character(cpath, real_path) From 754c6aeb41a3cdf88ec55179d23d8a686dfd76a1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 16:22:14 +0100 Subject: [PATCH 039/799] Export macro via fpm --- src/fpm_compiler.f90 | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index f6c02e9845..95aff6e8ee 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -37,7 +37,8 @@ module fpm_compiler OS_SOLARIS, & OS_FREEBSD, & OS_OPENBSD, & - OS_UNKNOWN + OS_UNKNOWN, & + os_is_unix use fpm_filesystem, only: join_path, basename, get_temp_filename, delete_file, unix_path, & & getline, run use fpm_strings, only: split, string_cat, string_t, str_ends_with, str_begins_with_str @@ -208,6 +209,13 @@ subroutine get_release_compile_flags(id, flags) integer(compiler_enum), intent(in) :: id character(len=:), allocatable, intent(out) :: flags + character(len=:), allocatable :: flag_gnu_win32 + + if (os_is_unix()) then + flag_gnu_win32 = "" + else + flag_gnu_win32 = " -D_WIN32" + end if select case(id) case default @@ -225,7 +233,8 @@ subroutine get_release_compile_flags(id, flags) flag_gnu_external//& flag_gnu_pic//& flag_gnu_limit//& - flag_gnu_coarray + flag_gnu_coarray//& + flag_gnu_win32 case(id_f95) flags = & @@ -304,6 +313,14 @@ subroutine get_debug_compile_flags(id, flags) integer(compiler_enum), intent(in) :: id character(len=:), allocatable, intent(out) :: flags + character(len=:), allocatable :: flag_gnu_win32 + + if (os_is_unix()) then + flag_gnu_win32 = "" + else + flag_gnu_win32 = " -D_WIN32" + end if + select case(id) case default flags = "" @@ -323,7 +340,8 @@ subroutine get_debug_compile_flags(id, flags) flag_gnu_debug//& flag_gnu_check//& flag_gnu_backtrace//& - flag_gnu_coarray + flag_gnu_coarray//& + flag_gnu_win32 case(id_f95) flags = & flag_gnu_warn//& From 1c3df19b557f08e3a90dd155e05d696875eac938 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 17:02:29 +0100 Subject: [PATCH 040/799] Add prints --- src/fpm_compiler.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 95aff6e8ee..0ab4b4234c 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -321,6 +321,10 @@ subroutine get_debug_compile_flags(id, flags) flag_gnu_win32 = " -D_WIN32" end if + print *, 'hey' + print *, flag_gnu_win32 + print *, id + select case(id) case default flags = "" From 4663fcec6f792a0f877ac6235c5f30cd5b427620 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 17:15:58 +0100 Subject: [PATCH 041/799] Test error --- src/fpm_compiler.f90 | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 0ab4b4234c..4e61220e00 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -316,15 +316,11 @@ subroutine get_debug_compile_flags(id, flags) character(len=:), allocatable :: flag_gnu_win32 if (os_is_unix()) then - flag_gnu_win32 = "" + flag_gnu_win32 = "abc" else flag_gnu_win32 = " -D_WIN32" end if - print *, 'hey' - print *, flag_gnu_win32 - print *, id - select case(id) case default flags = "" From 508672560766fc78ecde57ca46ed55b026b28fca Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 17:31:37 +0100 Subject: [PATCH 042/799] Add -cpp --- src/fpm_compiler.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 4e61220e00..953291c106 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -214,7 +214,7 @@ subroutine get_release_compile_flags(id, flags) if (os_is_unix()) then flag_gnu_win32 = "" else - flag_gnu_win32 = " -D_WIN32" + flag_gnu_win32 = " -cpp -D_WIN32" end if select case(id) @@ -316,9 +316,9 @@ subroutine get_debug_compile_flags(id, flags) character(len=:), allocatable :: flag_gnu_win32 if (os_is_unix()) then - flag_gnu_win32 = "abc" + flag_gnu_win32 = "" else - flag_gnu_win32 = " -D_WIN32" + flag_gnu_win32 = " -cpp -D_WIN32" end if select case(id) From a73e1ff1e4e673c3190a947af3bc165a33361597 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 18:05:46 +0100 Subject: [PATCH 043/799] Set default profiles --- src/fpm/manifest/profiles.f90 | 14 +++++++++++--- src/fpm_compiler.f90 | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 2e84f0c6e9..9d45499eb7 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -46,7 +46,7 @@ module fpm_manifest_profile use fpm_toml, only : toml_table, toml_key, toml_stat, get_value use fpm_strings, only: lower use fpm_environment, only: get_os_type, OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, os_is_unix use fpm_filesystem, only: join_path implicit none public :: profile_config_t, new_profile, new_profiles, get_default_profiles, & @@ -693,6 +693,13 @@ function get_default_profiles(error) result(default_profiles) type(error_t), allocatable, intent(out) :: error type(profile_config_t), allocatable :: default_profiles(:) + character(len=:), allocatable :: flag_gnu_win32 + + if (os_is_unix()) then + flag_gnu_win32 = '' + else + flag_gnu_win32 = ' -cpp -D_WIN32' + end if default_profiles = [ & & new_profile('release', & @@ -703,7 +710,8 @@ function get_default_profiles(error) result(default_profiles) & new_profile('release', & & 'gfortran', & & OS_ALL, & - & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=single', & + & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=single'//& + flag_gnu_win32, & & is_built_in=.true.), & & new_profile('release', & & 'f95', & @@ -759,7 +767,7 @@ function get_default_profiles(error) result(default_profiles) & 'gfortran', & & OS_ALL, & & flags = ' -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds& - & -fcheck=array-temps -fbacktrace -fcoarray=single', & + & -fcheck=array-temps -fbacktrace -fcoarray=single'//flag_gnu_win32, & & is_built_in=.true.), & & new_profile('debug', & & 'f95', & diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 953291c106..03263eed55 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -212,9 +212,9 @@ subroutine get_release_compile_flags(id, flags) character(len=:), allocatable :: flag_gnu_win32 if (os_is_unix()) then - flag_gnu_win32 = "" + flag_gnu_win32 = '' else - flag_gnu_win32 = " -cpp -D_WIN32" + flag_gnu_win32 = ' -cpp -D_WIN32' end if select case(id) @@ -316,9 +316,9 @@ subroutine get_debug_compile_flags(id, flags) character(len=:), allocatable :: flag_gnu_win32 if (os_is_unix()) then - flag_gnu_win32 = "" + flag_gnu_win32 = '' else - flag_gnu_win32 = " -cpp -D_WIN32" + flag_gnu_win32 = ' -cpp -D_WIN32' end if select case(id) From b28f44ee208319f35cc832d0176838553ef906f0 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 18:19:40 +0100 Subject: [PATCH 044/799] Revert --- src/fpm/manifest/profiles.f90 | 14 +++----------- src/fpm_compiler.f90 | 25 +++---------------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 9d45499eb7..2e84f0c6e9 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -46,7 +46,7 @@ module fpm_manifest_profile use fpm_toml, only : toml_table, toml_key, toml_stat, get_value use fpm_strings, only: lower use fpm_environment, only: get_os_type, OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, os_is_unix + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use fpm_filesystem, only: join_path implicit none public :: profile_config_t, new_profile, new_profiles, get_default_profiles, & @@ -693,13 +693,6 @@ function get_default_profiles(error) result(default_profiles) type(error_t), allocatable, intent(out) :: error type(profile_config_t), allocatable :: default_profiles(:) - character(len=:), allocatable :: flag_gnu_win32 - - if (os_is_unix()) then - flag_gnu_win32 = '' - else - flag_gnu_win32 = ' -cpp -D_WIN32' - end if default_profiles = [ & & new_profile('release', & @@ -710,8 +703,7 @@ function get_default_profiles(error) result(default_profiles) & new_profile('release', & & 'gfortran', & & OS_ALL, & - & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=single'//& - flag_gnu_win32, & + & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=single', & & is_built_in=.true.), & & new_profile('release', & & 'f95', & @@ -767,7 +759,7 @@ function get_default_profiles(error) result(default_profiles) & 'gfortran', & & OS_ALL, & & flags = ' -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds& - & -fcheck=array-temps -fbacktrace -fcoarray=single'//flag_gnu_win32, & + & -fcheck=array-temps -fbacktrace -fcoarray=single', & & is_built_in=.true.), & & new_profile('debug', & & 'f95', & diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 03263eed55..0374179de3 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -37,8 +37,7 @@ module fpm_compiler OS_SOLARIS, & OS_FREEBSD, & OS_OPENBSD, & - OS_UNKNOWN, & - os_is_unix + OS_UNKNOWN use fpm_filesystem, only: join_path, basename, get_temp_filename, delete_file, unix_path, & & getline, run use fpm_strings, only: split, string_cat, string_t, str_ends_with, str_begins_with_str @@ -209,14 +208,6 @@ subroutine get_release_compile_flags(id, flags) integer(compiler_enum), intent(in) :: id character(len=:), allocatable, intent(out) :: flags - character(len=:), allocatable :: flag_gnu_win32 - - if (os_is_unix()) then - flag_gnu_win32 = '' - else - flag_gnu_win32 = ' -cpp -D_WIN32' - end if - select case(id) case default flags = "" @@ -233,8 +224,7 @@ subroutine get_release_compile_flags(id, flags) flag_gnu_external//& flag_gnu_pic//& flag_gnu_limit//& - flag_gnu_coarray//& - flag_gnu_win32 + flag_gnu_coarray case(id_f95) flags = & @@ -313,14 +303,6 @@ subroutine get_debug_compile_flags(id, flags) integer(compiler_enum), intent(in) :: id character(len=:), allocatable, intent(out) :: flags - character(len=:), allocatable :: flag_gnu_win32 - - if (os_is_unix()) then - flag_gnu_win32 = '' - else - flag_gnu_win32 = ' -cpp -D_WIN32' - end if - select case(id) case default flags = "" @@ -340,8 +322,7 @@ subroutine get_debug_compile_flags(id, flags) flag_gnu_debug//& flag_gnu_check//& flag_gnu_backtrace//& - flag_gnu_coarray//& - flag_gnu_win32 + flag_gnu_coarray case(id_f95) flags = & flag_gnu_warn//& From d65365131dea81912c37dd1dc70a0e710d59b9e5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 20:33:25 +0100 Subject: [PATCH 045/799] Test things via CI --- src/fpm/manifest/profiles.f90 | 4 ++-- src/fpm_compiler.f90 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 2e84f0c6e9..137fb9e6fb 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -703,7 +703,7 @@ function get_default_profiles(error) result(default_profiles) & new_profile('release', & & 'gfortran', & & OS_ALL, & - & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=single', & + & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=singleabd', & & is_built_in=.true.), & & new_profile('release', & & 'f95', & @@ -759,7 +759,7 @@ function get_default_profiles(error) result(default_profiles) & 'gfortran', & & OS_ALL, & & flags = ' -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds& - & -fcheck=array-temps -fbacktrace -fcoarray=single', & + & -fcheck=array-temps -fbacktrace -fcoarray=singleabe', & & is_built_in=.true.), & & new_profile('debug', & & 'f95', & diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 0374179de3..01101ef743 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -132,7 +132,7 @@ module fpm_compiler end interface debug character(*), parameter :: & - flag_gnu_coarray = " -fcoarray=single", & + flag_gnu_coarray = " -fcoarray=singleabc", & flag_gnu_backtrace = " -fbacktrace", & flag_gnu_opt = " -O3 -funroll-loops", & flag_gnu_debug = " -g", & From 4fb6351529e2859a5fa862a22ffbb095a39ac7a3 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 19 Jan 2023 20:44:35 +0100 Subject: [PATCH 046/799] Revert --- src/fpm/manifest/profiles.f90 | 4 ++-- src/fpm_compiler.f90 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 137fb9e6fb..2e84f0c6e9 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -703,7 +703,7 @@ function get_default_profiles(error) result(default_profiles) & new_profile('release', & & 'gfortran', & & OS_ALL, & - & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=singleabd', & + & flags=' -O3 -Wimplicit-interface -fPIC -fmax-errors=1 -funroll-loops -fcoarray=single', & & is_built_in=.true.), & & new_profile('release', & & 'f95', & @@ -759,7 +759,7 @@ function get_default_profiles(error) result(default_profiles) & 'gfortran', & & OS_ALL, & & flags = ' -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds& - & -fcheck=array-temps -fbacktrace -fcoarray=singleabe', & + & -fcheck=array-temps -fbacktrace -fcoarray=single', & & is_built_in=.true.), & & new_profile('debug', & & 'f95', & diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90 index 01101ef743..0374179de3 100644 --- a/src/fpm_compiler.f90 +++ b/src/fpm_compiler.f90 @@ -132,7 +132,7 @@ module fpm_compiler end interface debug character(*), parameter :: & - flag_gnu_coarray = " -fcoarray=singleabc", & + flag_gnu_coarray = " -fcoarray=single", & flag_gnu_backtrace = " -fbacktrace", & flag_gnu_opt = " -O3 -funroll-loops", & flag_gnu_debug = " -g", & From 334f2936bea13f10c73b651272990cb4d85fe918 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 22 Jan 2023 20:30:08 +0100 Subject: [PATCH 047/799] Check macro in C --- src/fpm.f90 | 3 +-- src/fpm_os.F90 | 25 ++++++---------------- src/fpm_os.c | 16 ++++++++++++++ src/fpm_targets.f90 | 2 +- test/fpm_test/test_module_dependencies.f90 | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) create mode 100644 src/fpm_os.c diff --git a/src/fpm.f90 b/src/fpm.f90 index b9c0d2a874..494844aea5 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -16,8 +16,7 @@ module fpm use fpm_sources, only: add_executable_sources, add_sources_from_dir -use fpm_targets, only: targets_from_sources, & - resolve_target_linking, build_target_t, build_target_ptr, & +use fpm_targets, only: targets_from_sources, build_target_t, build_target_ptr, & FPM_TARGET_EXECUTABLE, FPM_TARGET_ARCHIVE use fpm_manifest, only : get_package_data, package_config_t use fpm_error, only : error_t, fatal_error, fpm_stop diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 7869752245..c3b7adfd7e 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -39,24 +39,15 @@ function getcwd_(buf, bufsize) result(path) & type(c_ptr) :: path end function getcwd_ - !> Unix only. For Windows, use `fullpath`. - function realpath(path, resolved_path) result(ptr) & - bind(C, name="realpath") - import :: c_ptr, c_char - character(kind=c_char, len=1), intent(in) :: path(*) - character(kind=c_char, len=1), intent(out) :: resolved_path(*) - type(c_ptr) :: ptr - end function realpath - - !> Windows only, use `realpath` on Unix. - function fullpath(resolved_path, path, maxLength) result(ptr) & - bind(C, name="_fullpath") + !> Determine the absolute, canonicalized path for a given path. + function realpath(path, resolved_path, maxLength) result(ptr) & + bind(C, name="get_realpath") import :: c_ptr, c_char, c_int - character(kind=c_char, len=1), intent(out) :: resolved_path(*) character(kind=c_char, len=1), intent(in) :: path(*) + character(kind=c_char, len=1), intent(out) :: resolved_path(*) integer(c_int), value, intent(in) :: maxLength type(c_ptr) :: ptr - end function fullpath + end function realpath end interface contains @@ -145,11 +136,7 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) -#ifndef _WIN32 - ptr = realpath(appended_path, cpath) -#else - ptr = fullpath(cpath, appended_path, buffersize) -#endif + ptr = realpath(appended_path, cpath, buffersize) if (c_associated(ptr)) then call c_f_character(cpath, real_path) diff --git a/src/fpm_os.c b/src/fpm_os.c new file mode 100644 index 0000000000..4b9e1002e5 --- /dev/null +++ b/src/fpm_os.c @@ -0,0 +1,16 @@ +#include + +/// @brief Determine the absolute, canonicalized path for a given path. +/// @param path +/// @param resolved_path +/// @param maxLength +/// @return +int get_realpath(char* path, char* resolved_path, int maxLength) { +// Checking macro in C because it doesn't work with gfortran on Windows, even +// when exported manually. +#ifndef _WIN32 + return realpath(path, resolved_path); +#else + return _fullpath(resolved_path, path, maxLength); +#endif +} \ No newline at end of file diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index c01cd4ee15..88ab4554f4 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -40,7 +40,7 @@ module fpm_targets FPM_TARGET_C_OBJECT, FPM_TARGET_CPP_OBJECT public build_target_t, build_target_ptr public targets_from_sources, resolve_module_dependencies -public resolve_target_linking, add_target, add_dependency +public add_target, add_dependency public filter_library_targets, filter_executable_targets, filter_modules diff --git a/test/fpm_test/test_module_dependencies.f90 b/test/fpm_test/test_module_dependencies.f90 index 805cc25590..3dab6d5c69 100644 --- a/test/fpm_test/test_module_dependencies.f90 +++ b/test/fpm_test/test_module_dependencies.f90 @@ -2,7 +2,7 @@ module test_module_dependencies use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_targets, only: targets_from_sources, resolve_module_dependencies, & - resolve_target_linking, build_target_t, build_target_ptr, & + build_target_t, build_target_ptr, & FPM_TARGET_EXECUTABLE, FPM_TARGET_OBJECT, FPM_TARGET_ARCHIVE use fpm_model, only: fpm_model_t, srcfile_t, & FPM_UNIT_UNKNOWN, FPM_UNIT_PROGRAM, FPM_UNIT_MODULE, & From d972ed44ea5e0c04ee3180fb22657d0a37a0d414 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 22 Jan 2023 21:02:32 +0100 Subject: [PATCH 048/799] Append backslash --- src/fpm_os.c | 2 +- test/fpm_test/test_os.f90 | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fpm_os.c b/src/fpm_os.c index 4b9e1002e5..986f267b1e 100644 --- a/src/fpm_os.c +++ b/src/fpm_os.c @@ -13,4 +13,4 @@ int get_realpath(char* path, char* resolved_path, int maxLength) { #else return _fullpath(resolved_path, path, maxLength); #endif -} \ No newline at end of file +} diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index c69c71f0e7..51a94bb77d 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -141,6 +141,8 @@ subroutine abs_path_root(error) end if else call env_variable(home_drive, 'HOMEDRIVE') + home_drive = home_drive//'\' + call get_absolute_path(home_drive, result, error) if (result /= home_drive) then From 9cb42891eb2c657c8f6f4b5cc7fffa7348ba773c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 22 Jan 2023 21:03:17 +0100 Subject: [PATCH 049/799] Call it home_path --- test/fpm_test/test_os.f90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index 51a94bb77d..800ebf50a9 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -131,6 +131,7 @@ subroutine abs_path_root(error) type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: result character(len=:), allocatable :: home_drive + character(len=:), allocatable :: home_path if (os_is_unix()) then call get_absolute_path('/', result, error) @@ -141,12 +142,12 @@ subroutine abs_path_root(error) end if else call env_variable(home_drive, 'HOMEDRIVE') - home_drive = home_drive//'\' + home_path = home_drive//'\' - call get_absolute_path(home_drive, result, error) + call get_absolute_path(home_path, result, error) if (result /= home_drive) then - call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_drive//"'") + call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'") return end if end if From aae8fdf22e4a6e578b401d06f732bca45c84dec7 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 22 Jan 2023 21:15:33 +0100 Subject: [PATCH 050/799] Fix if condition --- test/fpm_test/test_os.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index 800ebf50a9..c3b7c2e2af 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -146,7 +146,7 @@ subroutine abs_path_root(error) call get_absolute_path(home_path, result, error) - if (result /= home_drive) then + if (result /= home_path) then call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'") return end if From 899f5c86e871cf17184718b35203f4bf4eb3a5f7 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 22 Jan 2023 21:24:04 +0100 Subject: [PATCH 051/799] Rename get_realpath to get_fullpath for uniqueness --- src/fpm_os.F90 | 2 +- src/fpm_os.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index c3b7adfd7e..9c0d055f4f 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -41,7 +41,7 @@ end function getcwd_ !> Determine the absolute, canonicalized path for a given path. function realpath(path, resolved_path, maxLength) result(ptr) & - bind(C, name="get_realpath") + bind(C, name="get_fullpath") import :: c_ptr, c_char, c_int character(kind=c_char, len=1), intent(in) :: path(*) character(kind=c_char, len=1), intent(out) :: resolved_path(*) diff --git a/src/fpm_os.c b/src/fpm_os.c index 986f267b1e..4e3ec78263 100644 --- a/src/fpm_os.c +++ b/src/fpm_os.c @@ -5,7 +5,7 @@ /// @param resolved_path /// @param maxLength /// @return -int get_realpath(char* path, char* resolved_path, int maxLength) { +int get_fullpath(char* path, char* resolved_path, int maxLength) { // Checking macro in C because it doesn't work with gfortran on Windows, even // when exported manually. #ifndef _WIN32 From 44ca6355ccd2d5ea02a55dcf0c1fd0cbc435a29b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 22 Jan 2023 22:50:56 +0100 Subject: [PATCH 052/799] Allow bootstrapping --- src/fpm_os.F90 | 22 ++++++++++++++++++---- src/fpm_os.c | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 9c0d055f4f..86a1625235 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -39,15 +39,24 @@ function getcwd_(buf, bufsize) result(path) & type(c_ptr) :: path end function getcwd_ + !> Determine the absolute, canonicalized path for a given path. Unix-only. + function realpath(path, resolved_path) result(ptr) bind(C) + import :: c_ptr, c_char, c_int + character(kind=c_char, len=1), intent(in) :: path(*) + character(kind=c_char, len=1), intent(out) :: resolved_path(*) + type(c_ptr) :: ptr + end function realpath + !> Determine the absolute, canonicalized path for a given path. - function realpath(path, resolved_path, maxLength) result(ptr) & - bind(C, name="get_fullpath") + !> Calls custom C routine and is able to distinguish between Unix and Windows. + function c_realpath(path, resolved_path, maxLength) result(ptr) & + bind(C, name="c_realpath") import :: c_ptr, c_char, c_int character(kind=c_char, len=1), intent(in) :: path(*) character(kind=c_char, len=1), intent(out) :: resolved_path(*) integer(c_int), value, intent(in) :: maxLength type(c_ptr) :: ptr - end function realpath + end function c_realpath end interface contains @@ -136,7 +145,12 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) - ptr = realpath(appended_path, cpath, buffersize) +! Using gfortran, _WIN32 is currently not correctly exported on Windows +#if defined(FPM_BOOTSTRAP) && !defined(_WIN32) + ptr = realpath(appended_path, cpath) +#else + ptr = c_realpath(appended_path, cpath, buffersize) +#endif if (c_associated(ptr)) then call c_f_character(cpath, real_path) diff --git a/src/fpm_os.c b/src/fpm_os.c index 4e3ec78263..2d417a0695 100644 --- a/src/fpm_os.c +++ b/src/fpm_os.c @@ -5,7 +5,7 @@ /// @param resolved_path /// @param maxLength /// @return -int get_fullpath(char* path, char* resolved_path, int maxLength) { +int c_realpath(char* path, char* resolved_path, int maxLength) { // Checking macro in C because it doesn't work with gfortran on Windows, even // when exported manually. #ifndef _WIN32 From 38ea7f47da27bfa3029e90b50b49a981d8af873a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 23 Jan 2023 14:12:39 +0100 Subject: [PATCH 053/799] Fix linting --- app/main.f90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index af8d6ed898..dc761c848f 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -104,20 +104,20 @@ function has_manifest(dir) has_manifest = exists(join_path(dir, "fpm.toml")) end function has_manifest - subroutine handle_error(error) - type(error_t), optional, intent(in) :: error - if (present(error)) then - write(error_unit, '("[Error]", 1x, a)') error%message + subroutine handle_error(e) + type(error_t), optional, intent(in) :: e + if (present(e)) then + write (error_unit, '("[Error]", 1x, a)') e%message stop 1 end if end subroutine handle_error !> Save access to working directory in settings, in case setting have not been allocated - subroutine get_working_dir(settings, working_dir) + subroutine get_working_dir(settings, w_dir) class(fpm_cmd_settings), optional, intent(in) :: settings - character(len=:), allocatable, intent(out) :: working_dir + character(len=:), allocatable, intent(out) :: w_dir if (present(settings)) then - working_dir = settings%working_dir + w_dir = settings%working_dir end if end subroutine get_working_dir From 4f8ae18d4ad2a582f76f73ee180d5087009dcf04 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 23 Jan 2023 14:34:07 +0100 Subject: [PATCH 054/799] Remove unused variables --- src/fpm/cmd/install.f90 | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/fpm/cmd/install.f90 b/src/fpm/cmd/install.f90 index f81b4dfc44..c260bfc4df 100644 --- a/src/fpm/cmd/install.f90 +++ b/src/fpm/cmd/install.f90 @@ -28,7 +28,6 @@ subroutine cmd_install(settings) type(fpm_model_t) :: model type(build_target_ptr), allocatable :: targets(:) type(installer_t) :: installer - character(len=:), allocatable :: lib, dir type(string_t), allocatable :: list(:) logical :: installable @@ -49,7 +48,7 @@ subroutine cmd_install(settings) end if if (settings%list) then - call install_info(output_unit, package, model, targets) + call install_info(output_unit, targets) return end if @@ -81,14 +80,11 @@ subroutine cmd_install(settings) end subroutine cmd_install - subroutine install_info(unit, package, model, targets) + subroutine install_info(unit, targets) integer, intent(in) :: unit - type(package_config_t), intent(in) :: package - type(fpm_model_t), intent(in) :: model type(build_target_ptr), intent(in) :: targets(:) integer :: ii, ntargets - character(len=:), allocatable :: lib type(string_t), allocatable :: install_target(:), temp(:) allocate(install_target(0)) From dfc328020f6ef0976d6224c28accfdc2f5f11633 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 24 Jan 2023 19:01:45 +0100 Subject: [PATCH 055/799] Clean up and add version and namespace to dependency_config_t --- app/main.f90 | 5 ----- src/fpm.f90 | 10 +++------- src/fpm/manifest/dependency.f90 | 28 +++++++++++++++++++--------- test/cli_test/cli_test.f90 | 2 +- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index dc761c848f..69ecbe2b55 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -17,20 +17,15 @@ program main use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update use fpm_os, only: change_directory, get_current_directory -use fpm_settings, only: fpm_global_settings, get_global_settings implicit none class(fpm_cmd_settings), allocatable :: cmd_settings -type(fpm_global_settings), allocatable :: global_settings type(error_t), allocatable :: error character(len=:), allocatable :: pwd_start, pwd_working, working_dir, project_root call get_command_line_settings(cmd_settings) -call get_global_settings(global_settings, error) -call handle_error(error) - call get_current_directory(pwd_start, error) call handle_error(error) diff --git a/src/fpm.f90 b/src/fpm.f90 index 494844aea5..cc41299d53 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -31,10 +31,8 @@ module fpm contains - +!> Constructs a valid fpm model from command line settings and the toml manifest. subroutine build_model(model, settings, package, error) - ! Constructs a valid fpm model from command line settings and toml manifest - ! type(fpm_model_t), intent(out) :: model type(fpm_build_settings), intent(in) :: settings type(package_config_t), intent(in) :: package @@ -42,10 +40,8 @@ subroutine build_model(model, settings, package, error) integer :: i, j type(package_config_t) :: dependency - character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags - character(len=:), allocatable :: version + character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags, version logical :: has_cpp - logical :: duplicates_found = .false. type(string_t) :: include_dir @@ -221,7 +217,6 @@ subroutine build_model(model, settings, package, error) endif - if (settings%verbose) then write(*,*)' BUILD_NAME: ',model%build_prefix write(*,*)' COMPILER: ',model%compiler%fc @@ -287,6 +282,7 @@ end subroutine check_modules_for_duplicates subroutine cmd_build(settings) type(fpm_build_settings), intent(in) :: settings + type(package_config_t) :: package type(fpm_model_t) :: model type(build_target_ptr), allocatable :: targets(:) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 00f136472f..0028ac437c 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -44,6 +44,15 @@ module fpm_manifest_dependency !> Local target character(len=:), allocatable :: path + !> Namespace which the dependency belongs to. + !> Enables multiple dependencies with the same name. + !> Required for dependencies that are obtained via the official registry. + character(len=:), allocatable :: namespace + + !> The specified version of the dependency. + !> The latest version is used if not specified. + character(len=:), allocatable :: version + !> Git descriptor type(git_target_t), allocatable :: git @@ -138,31 +147,31 @@ subroutine check(table, error) call table%get_keys(list) if (size(list) < 1) then - call syntax_error(error, "Dependency "//name//" does not provide sufficient entries") + call syntax_error(error, "Dependency '"//name//"' does not provide sufficient entries") return end if do ikey = 1, size(list) select case(list(ikey)%key) case default - call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in dependency "//name) + call syntax_error(error, "Key '"//list(ikey)%key//"' is not allowed in dependency '"//name//"'") exit - + case("git") if (url_present) then - call syntax_error(error, "Dependency "//name//" cannot have both git and path entries") + call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") exit end if call get_value(table, "git", url) if (.not.allocated(url)) then - call syntax_error(error, "Dependency "//name//" has invalid git source") + call syntax_error(error, "Dependency '"//name//"' has invalid git source") exit end if url_present = .true. case("path") if (url_present) then - call syntax_error(error, "Dependency "//name//" cannot have both git and path entries") + call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") exit end if url_present = .true. @@ -170,7 +179,7 @@ subroutine check(table, error) case("branch", "rev", "tag") if (git_target_present) then - call syntax_error(error, "Dependency "//name//" can only have one of branch, rev or tag present") + call syntax_error(error, "Dependency '"//name//"' can only have one of branch, rev or tag present") exit end if git_target_present = .true. @@ -180,12 +189,13 @@ subroutine check(table, error) if (allocated(error)) return if (.not.url_present) then - call syntax_error(error, "Dependency "//name//" does not provide a method to actually retrieve itself") + call syntax_error(error, "Dependency '"//name//"' does not provide a method to actually retrieve itself") return end if if (has_path .and. git_target_present) then - call syntax_error(error, "Dependency "//name//" uses a local path, therefore no git identifiers are allowed") + call syntax_error(error, "Dependency '"//name//"' uses a local path, therefore no git identifiers are allowed") + return end if end subroutine check diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index 4fa8e3acf2..9f82cb7056 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -206,7 +206,7 @@ subroutine parse() fpm_clean_settings, & fpm_install_settings, & get_command_line_settings -use fpm, only: cmd_build, cmd_run, cmd_clean +use fpm, only: cmd_run, cmd_clean use fpm_cmd_install, only: cmd_install use fpm_cmd_new, only: cmd_new class(fpm_cmd_settings), allocatable :: cmd_settings From ebaa49c94e885ebb6460ebbc97042b7f0336f484 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 24 Jan 2023 19:05:12 +0100 Subject: [PATCH 056/799] Rename version to vers to avoid conflict --- src/fpm/manifest/dependency.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 0028ac437c..cfbc51241b 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -51,7 +51,7 @@ module fpm_manifest_dependency !> The specified version of the dependency. !> The latest version is used if not specified. - character(len=:), allocatable :: version + character(len=:), allocatable :: vers !> Git descriptor type(git_target_t), allocatable :: git From ab52dc840e0a9d39137d2652fe12c1d8ddcda305 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 24 Jan 2023 21:35:42 +0100 Subject: [PATCH 057/799] Refactor table checking for higher extensibility --- src/fpm/manifest/dependency.f90 | 78 ++++++++++++++++----------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index cfbc51241b..6c35dcb89f 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -136,12 +136,18 @@ subroutine check(table, error) character(len=:), allocatable :: name, url type(toml_key), allocatable :: list(:) - logical :: url_present, git_target_present, has_path integer :: ikey - has_path = .false. - url_present = .false. - git_target_present = .false. + !> List of allowed keys for the dependency table + character(*), dimension(*), parameter :: valid_keys = [character(24) ::& + & "namespace",& + "vers",& + "path",& + "git",& + "tag",& + "branch",& + "rev" & + & ] call table%get_key(name) call table%get_keys(list) @@ -152,54 +158,44 @@ subroutine check(table, error) end if do ikey = 1, size(list) - select case(list(ikey)%key) - case default + if (.not. any(list(ikey)%key == valid_keys)) then call syntax_error(error, "Key '"//list(ikey)%key//"' is not allowed in dependency '"//name//"'") - exit - - case("git") - if (url_present) then - call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") - exit - end if - call get_value(table, "git", url) - if (.not.allocated(url)) then - call syntax_error(error, "Dependency '"//name//"' has invalid git source") - exit - end if - url_present = .true. - - case("path") - if (url_present) then - call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") - exit - end if - url_present = .true. - has_path = .true. - - case("branch", "rev", "tag") - if (git_target_present) then - call syntax_error(error, "Dependency '"//name//"' can only have one of branch, rev or tag present") - exit - end if - git_target_present = .true. - - end select + return + end if end do - if (allocated(error)) return - if (.not.url_present) then + if (table%has_key("path") .and. table%has_key("git")) then + call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") + return + end if + + if ((table%has_key("branch") .and. table%has_key("rev")) .or.& + (table%has_key("branch") .and. table%has_key("tag")) .or.& + (table%has_key("rev") .and. table%has_key("tag"))) then + call syntax_error(error, "Dependency '"//name//"' can only have one of branch, rev or tag present") + return + end if + + if (.not. table%has_key("path") .and. .not. table%has_key("git")) then call syntax_error(error, "Dependency '"//name//"' does not provide a method to actually retrieve itself") return end if - if (has_path .and. git_target_present) then - call syntax_error(error, "Dependency '"//name//"' uses a local path, therefore no git identifiers are allowed") + if (.not. table%has_key("git") .and. (table%has_key("branch") .or.& + table%has_key("tag") .or. table%has_key("rev"))) then + call syntax_error(error, "Dependency '"//name//"' has git identifier but no git entry") return end if - end subroutine check + if (table%has_key("git")) then + call get_value(table, "git", url) + if (.not. allocated(url)) then + call syntax_error(error, "Dependency '"//name//"' has invalid git source") + return + end if + end if + end subroutine check !> Construct new dependency array from a TOML data structure subroutine new_dependencies(deps, table, root, error) From 3c447ccd1cc01941bd896ee7f09cb1c12176cfdd Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 24 Jan 2023 22:50:51 +0100 Subject: [PATCH 058/799] Fix linting --- test/fpm_test/test_manifest.f90 | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index e608e79fcf..1aaa7aa505 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1,27 +1,23 @@ !> Define tests for the `fpm_manifest` modules module test_manifest use fpm_filesystem, only: get_temp_filename - use testsuite, only : new_unittest, unittest_t, error_t, test_failed, & - & check_string + use testsuite, only : new_unittest, unittest_t, error_t, test_failed, check_string use fpm_manifest use fpm_manifest_profile, only: profile_config_t, find_profile use fpm_strings, only: operator(.in.) implicit none private - public :: collect_manifest - contains - !> Collect all exported unit tests - subroutine collect_manifest(testsuite) + subroutine collect_manifest(tests) !> Collection of tests - type(unittest_t), allocatable, intent(out) :: testsuite(:) + type(unittest_t), allocatable, intent(out) :: tests(:) - testsuite = [ & + tests = [ & & new_unittest("valid-manifest", test_valid_manifest), & & new_unittest("invalid-manifest", test_invalid_manifest, should_fail=.true.), & & new_unittest("default-library", test_default_library), & @@ -361,7 +357,6 @@ subroutine test_dependency_invalid_git(error) type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat type(dependency_config_t) :: dependency call new_table(table) @@ -523,7 +518,6 @@ subroutine test_profiles_keyvalue_table(error) type(package_config_t) :: package character(len=*), parameter :: manifest = 'fpm-profiles-error.toml' integer :: unit - character(:), allocatable :: profile_name, compiler, flags open(file=manifest, newunit=unit) write(unit, '(a)') & @@ -1289,7 +1283,6 @@ subroutine test_macro_parsing(error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: flags character(len=:), allocatable :: version type(package_config_t) :: package From 4bbf4df494aa33bbe5f5cf57734dd3c22aff8d82 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 24 Jan 2023 22:58:11 +0100 Subject: [PATCH 059/799] Improve error message for all keys --- src/fpm/manifest/dependency.f90 | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 6c35dcb89f..659a0c1875 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -134,7 +134,7 @@ subroutine check(table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: name, url + character(len=:), allocatable :: name, value type(toml_key), allocatable :: list(:) integer :: ikey @@ -162,6 +162,13 @@ subroutine check(table, error) call syntax_error(error, "Key '"//list(ikey)%key//"' is not allowed in dependency '"//name//"'") return end if + + ! Check if value can be mapped or else show error message with the error location + call get_value(table, list(ikey)%key, value) + if (.not. allocated(value)) then + call syntax_error(error, "Dependency '"//name//"' has invalid '"//list(ikey)%key//"' entry") + return + end if end do if (table%has_key("path") .and. table%has_key("git")) then @@ -187,14 +194,6 @@ subroutine check(table, error) return end if - if (table%has_key("git")) then - call get_value(table, "git", url) - if (.not. allocated(url)) then - call syntax_error(error, "Dependency '"//name//"' has invalid git source") - return - end if - end if - end subroutine check !> Construct new dependency array from a TOML data structure From 9caa8643d64bd398b7f2627deb0ab996efc53291 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 24 Jan 2023 23:33:10 +0100 Subject: [PATCH 060/799] Further improve error message --- src/fpm/manifest/dependency.f90 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 659a0c1875..b55193c19b 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -134,9 +134,9 @@ subroutine check(table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: name, value + character(len=:), allocatable :: name, value, valid_keys_string type(toml_key), allocatable :: list(:) - integer :: ikey + integer :: ikey, ivalid !> List of allowed keys for the dependency table character(*), dimension(*), parameter :: valid_keys = [character(24) ::& @@ -159,7 +159,13 @@ subroutine check(table, error) do ikey = 1, size(list) if (.not. any(list(ikey)%key == valid_keys)) then - call syntax_error(error, "Key '"//list(ikey)%key//"' is not allowed in dependency '"//name//"'") + ! Create nicer error message + valid_keys_string = new_line('a')//new_line('a') + do ivalid = 1, size(valid_keys) + valid_keys_string = valid_keys_string//trim(valid_keys(ivalid))//new_line('a') + end do + call syntax_error(error, "Key '"//list(ikey)%key//"' not allowed in dependency '"//& + name//"'."//new_line('a')//new_line('a')//'Valid keys: '//valid_keys_string) return end if From d904045b27dab90a9d4198c0bc4498f460a5606b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 25 Jan 2023 00:01:12 +0100 Subject: [PATCH 061/799] Check namespace and add test --- src/fpm/manifest/dependency.f90 | 16 +++++++++------- test/fpm_test/test_manifest.f90 | 23 ++++++++++++++++++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index b55193c19b..55ba9677a9 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -159,7 +159,7 @@ subroutine check(table, error) do ikey = 1, size(list) if (.not. any(list(ikey)%key == valid_keys)) then - ! Create nicer error message + ! Improve error message valid_keys_string = new_line('a')//new_line('a') do ivalid = 1, size(valid_keys) valid_keys_string = valid_keys_string//trim(valid_keys(ivalid))//new_line('a') @@ -189,14 +189,16 @@ subroutine check(table, error) return end if - if (.not. table%has_key("path") .and. .not. table%has_key("git")) then - call syntax_error(error, "Dependency '"//name//"' does not provide a method to actually retrieve itself") + if ((table%has_key("branch") .or. table%has_key("tag") .or. table%has_key("rev"))& + .and. .not. table%has_key("git")) then + call syntax_error(error, "Dependency '"//name//"' has git identifier but no git url") return end if - - if (.not. table%has_key("git") .and. (table%has_key("branch") .or.& - table%has_key("tag") .or. table%has_key("rev"))) then - call syntax_error(error, "Dependency '"//name//"' has git identifier but no git entry") + + if (.not. table%has_key("path") .and. .not. table%has_key("git")& + .and. .not. table%has_key("namespace")) then + call syntax_error(error, "Please provide a 'namespace' for dependency '"//name//& + "' if it is not a local path or git repository") return end if diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 1aaa7aa505..2a923d421f 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -28,6 +28,7 @@ subroutine collect_manifest(tests) & new_unittest("dependency-nourl", test_dependency_nourl, should_fail=.true.), & & new_unittest("dependency-gitconflict", test_dependency_gitconflict, should_fail=.true.), & & new_unittest("dependency-invalid-git", test_dependency_invalid_git, should_fail=.true.), & + & new_unittest("dependency-no-namespace", test_dependency_no_namespace, should_fail=.true.), & & new_unittest("dependency-wrongkey", test_dependency_wrongkey, should_fail=.true.), & & new_unittest("dependencies-empty", test_dependencies_empty), & & new_unittest("dependencies-typeerror", test_dependencies_typeerror, should_fail=.true.), & @@ -356,18 +357,34 @@ subroutine test_dependency_invalid_git(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table - type(toml_table), pointer :: child type(dependency_config_t) :: dependency call new_table(table) table%key = 'example' - call add_table(table, 'git', child) - call set_value(child, 'path', '../../package') + call set_value(table, 'git', 123) ! Not a string call new_dependency(dependency, table, error=error) end subroutine test_dependency_invalid_git + !> Namespace is necessary if a dependency is not a git or path dependency + subroutine test_dependency_no_namespace(error) + use fpm_manifest_dependency + use fpm_toml, only : new_table, add_table, toml_table, set_value + + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_config_t) :: dependency + + call new_table(table) + table%key = 'example' + call set_value(table, 'vers', 'abc') + + call new_dependency(dependency, table, error=error) + + end subroutine test_dependency_no_namespace + !> Try to create a dependency with conflicting entries subroutine test_dependency_wrongkey(error) From 495828cdbd10acd28f1549b228e8ce0e0e4846c3 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 25 Jan 2023 00:12:18 +0100 Subject: [PATCH 062/799] Read vers and add test --- src/fpm/manifest/dependency.f90 | 5 +++++ test/fpm_test/test_manifest.f90 | 24 ++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 55ba9677a9..cc0ebc3f13 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -202,6 +202,11 @@ subroutine check(table, error) return end if + if (table%has_key('vers') .and. (table%has_key('path') .or. table%has_key('git'))) then + call syntax_error(error, "Dependency '"//name//"' cannot have both vers and git/path entries") + return + end if + end subroutine check !> Construct new dependency array from a TOML data structure diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 2a923d421f..a58fbfb193 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -29,6 +29,7 @@ subroutine collect_manifest(tests) & new_unittest("dependency-gitconflict", test_dependency_gitconflict, should_fail=.true.), & & new_unittest("dependency-invalid-git", test_dependency_invalid_git, should_fail=.true.), & & new_unittest("dependency-no-namespace", test_dependency_no_namespace, should_fail=.true.), & + & new_unittest("dependency-redundant-vers", test_dependency_redundant_vers, should_fail=.true.), & & new_unittest("dependency-wrongkey", test_dependency_wrongkey, should_fail=.true.), & & new_unittest("dependencies-empty", test_dependencies_empty), & & new_unittest("dependencies-typeerror", test_dependencies_typeerror, should_fail=.true.), & @@ -351,7 +352,7 @@ end subroutine test_dependency_gitconflict !> Try to create a git dependency with invalid source format subroutine test_dependency_invalid_git(error) use fpm_manifest_dependency - use fpm_toml, only : new_table, add_table, toml_table, set_value + use fpm_toml, only : new_table, toml_table, set_value !> Error handling type(error_t), allocatable, intent(out) :: error @@ -370,7 +371,7 @@ end subroutine test_dependency_invalid_git !> Namespace is necessary if a dependency is not a git or path dependency subroutine test_dependency_no_namespace(error) use fpm_manifest_dependency - use fpm_toml, only : new_table, add_table, toml_table, set_value + use fpm_toml, only : new_table, toml_table, set_value type(error_t), allocatable, intent(out) :: error @@ -385,6 +386,25 @@ subroutine test_dependency_no_namespace(error) end subroutine test_dependency_no_namespace + !> Do not specify version with a git or path dependency + subroutine test_dependency_redundant_vers(error) + use fpm_manifest_dependency + use fpm_toml, only : new_table, toml_table, set_value + + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_config_t) :: dependency + + call new_table(table) + table%key = 'example' + call set_value(table, 'vers', '0.0.0') + call set_value(table, 'path', 'abc') + + call new_dependency(dependency, table, error=error) + + end subroutine test_dependency_redundant_vers + !> Try to create a dependency with conflicting entries subroutine test_dependency_wrongkey(error) From 010ea33256be46baa002419fa53298be4af36031 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 25 Jan 2023 02:05:19 +0100 Subject: [PATCH 063/799] Load namespace and vers into model --- src/fpm/manifest/dependency.f90 | 43 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index cc0ebc3f13..efed2041ee 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -82,46 +82,51 @@ subroutine new_dependency(self, table, root, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: url, obj + character(len=:), allocatable :: uri, value call check(table, error) if (allocated(error)) return call table%get_key(self%name) + call get_value(table, "namespace", self%namespace) - call get_value(table, "path", url) - if (allocated(url)) then - if (get_os_type() == OS_WINDOWS) url = windows_path(url) - if (present(root)) url = root//url ! Relative to the fpm.toml it’s written in - call move_alloc(url, self%path) - else - call get_value(table, "git", url) + call get_value(table, "path", uri) + if (allocated(uri)) then + if (get_os_type() == OS_WINDOWS) uri = windows_path(uri) + if (present(root)) uri = root//uri ! Relative to the fpm.toml it’s written in + call move_alloc(uri, self%path) + return + end if - call get_value(table, "tag", obj) - if (allocated(obj)) then - self%git = git_target_tag(url, obj) + call get_value(table, "git", uri) + if (allocated(uri)) then + call get_value(table, "tag", value) + if (allocated(value)) then + self%git = git_target_tag(uri, value) end if if (.not.allocated(self%git)) then - call get_value(table, "branch", obj) - if (allocated(obj)) then - self%git = git_target_branch(url, obj) + call get_value(table, "branch", value) + if (allocated(value)) then + self%git = git_target_branch(uri, value) end if end if if (.not.allocated(self%git)) then - call get_value(table, "rev", obj) - if (allocated(obj)) then - self%git = git_target_revision(url, obj) + call get_value(table, "rev", value) + if (allocated(value)) then + self%git = git_target_revision(uri, value) end if end if if (.not.allocated(self%git)) then - self%git = git_target_default(url) + self%git = git_target_default(uri) end if - + return end if + call get_value(table, "vers", self%vers) + end subroutine new_dependency From 9b52a22b7332fb9c9c50df86d0db117b1b82ca82 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 25 Jan 2023 15:38:15 +0100 Subject: [PATCH 064/799] Refactor find_dependency for improved readability --- src/fpm/dependency.f90 | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index bd85b6f014..7db803d276 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -131,12 +131,14 @@ module fpm_dependency generic :: resolve => resolve_dependencies, resolve_dependency !> Resolve dependencies procedure, private :: resolve_dependencies - !> Resolve dependencies + !> Resolve dependency procedure, private :: resolve_dependency + !> True if entity can be found + generic :: has => has_dependency + !> True if dependency is part of the tree + procedure, private :: has_dependency !> Find a dependency in the tree - generic :: find => find_dependency, find_name - !> Find a dependency from an dependency configuration - procedure, private :: find_dependency + generic :: find => find_name !> Find a dependency by its name procedure, private :: find_name !> Depedendncy resolution finished @@ -232,7 +234,7 @@ subroutine add_project(self, package, error) type(error_t), allocatable, intent(out) :: error type(dependency_config_t) :: dependency - character(len=:), allocatable :: root + character(len=*), parameter :: root = '.' if (allocated(self%cache)) then call self%load(self%cache, error) @@ -243,8 +245,6 @@ subroutine add_project(self, package, error) call mkdir(self%dep_dir) end if - root = "." - ! Create this project as the first dependency node (depth 0) dependency%name = package%name dependency%path = root @@ -365,10 +365,7 @@ pure subroutine add_dependency(self, dependency, error) !> Error handling type(error_t), allocatable, intent(out) :: error - integer :: id - - id = self%find(dependency) - if (id == 0) then + if (.not. self%has(dependency)) then self%ndep = self%ndep + 1 call new_dependency_node(self%dep(self%ndep), dependency) end if @@ -496,18 +493,16 @@ subroutine resolve_dependency(self, dependency, root, error) end subroutine resolve_dependency - !> Find a dependency in the dependency tree - pure function find_dependency(self, dependency) result(pos) + !> True if dependency is part of the tree + pure logical function has_dependency(self, dependency) !> Instance of the dependency tree class(dependency_tree_t), intent(in) :: self - !> Dependency configuration to add + !> Dependency configuration to check class(dependency_config_t), intent(in) :: dependency - !> Index of the dependency - integer :: pos - pos = self%find(dependency%name) + has_dependency = self%find(dependency%name) /= 0 - end function find_dependency + end function has_dependency !> Find a dependency in the dependency tree pure function find_name(self, name) result(pos) From 1f604b43a1a03aa52fd4a07392f6d304063c36bb Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 25 Jan 2023 19:56:27 +0100 Subject: [PATCH 065/799] Clean up procedure --- src/fpm/dependency.f90 | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 7db803d276..aafa0c7a9c 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -453,20 +453,16 @@ subroutine resolve_dependency(self, dependency, root, error) if (dependency%done) return - fetch = .false. if (allocated(dependency%proj_dir)) then proj_dir = dependency%proj_dir - else - if (allocated(dependency%path)) then - proj_dir = join_path(root, dependency%path) - else if (allocated(dependency%git)) then - proj_dir = join_path(self%dep_dir, dependency%name) - fetch = .not.exists(proj_dir) - if (fetch) then - call dependency%git%checkout(proj_dir, error) - if (allocated(error)) return - end if - + else if (allocated(dependency%path)) then + proj_dir = join_path(root, dependency%path) + else if (allocated(dependency%git)) then + proj_dir = join_path(self%dep_dir, dependency%name) + fetch = .not. exists(proj_dir) + if (fetch) then + call dependency%git%checkout(proj_dir, error) + if (allocated(error)) return end if end if From 4115b17b16dde2499245c76b34120db6cfa9aecc Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 27 Jan 2023 18:32:59 +0100 Subject: [PATCH 066/799] Improve get_global_settings --- src/fpm_settings.f90 | 53 +++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index f828a1156f..757f32bcb1 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -12,12 +12,12 @@ module fpm_settings type :: fpm_global_settings !> Path to the global config file excluding the file name. - character(len=:), allocatable :: path_to_folder + character(len=:), allocatable :: path_to_config_folder !> Name of the global config file. The default is `config.toml`. - character(len=:), allocatable :: file_name + character(len=:), allocatable :: config_file_name type(fpm_registry_settings), allocatable :: registry_settings contains - procedure :: full_path + procedure :: has_custom_location, full_path end type type :: fpm_registry_settings @@ -31,7 +31,7 @@ module fpm_settings !> Obtain global settings from the global config file. subroutine get_global_settings(global_settings, error) !> Global settings to be obtained. - type(fpm_global_settings), allocatable, intent(inout) :: global_settings + type(fpm_global_settings), intent(inout) :: global_settings !> Error reading config file. type(error_t), allocatable, intent(out) :: error !> Absolute path to the config file. @@ -41,13 +41,11 @@ subroutine get_global_settings(global_settings, error) !> Error parsing to TOML table. type(toml_error), allocatable :: parse_error - if (.not. allocated(global_settings)) allocate (global_settings) - ! Use custom path to the config file if it was specified. - if (allocated(global_settings%path_to_folder) .and. allocated(global_settings%file_name)) then + if (global_settings%has_custom_location()) then ! Throw error if folder doesn't exist. - if (.not. exists(global_settings%path_to_folder)) then - call fatal_error(error, 'Folder not found: "'//global_settings%path_to_folder//'"') + if (.not. exists(global_settings%path_to_config_folder)) then + call fatal_error(error, 'Folder not found: "'//global_settings%path_to_config_folder//'"') return end if @@ -58,33 +56,25 @@ subroutine get_global_settings(global_settings, error) end if ! Make sure that the path to the global config file is absolute. - call get_absolute_path(global_settings%path_to_folder, abs_path_to_config, error) + call get_absolute_path(global_settings%path_to_config_folder, abs_path_to_config, error) if (allocated(error)) return - global_settings%path_to_folder = abs_path_to_config + global_settings%path_to_config_folder = abs_path_to_config else ! Use default path if it wasn't specified. if (os_is_unix()) then - global_settings%path_to_folder = join_path(get_local_prefix(), 'share', 'fpm') + global_settings%path_to_config_folder = join_path(get_local_prefix(), 'share', 'fpm') else - global_settings%path_to_folder = join_path(get_local_prefix(), 'fpm') + global_settings%path_to_config_folder = join_path(get_local_prefix(), 'fpm') end if ! Use default file name. - global_settings%file_name = 'config.toml' + global_settings%config_file_name = 'config.toml' - ! Deallocate and return if path doesn't exist. - if (.not. exists(global_settings%path_to_folder)) then - deallocate (global_settings%path_to_folder) - deallocate (global_settings%file_name) - return - end if + ! Return if path or file doesn't exist. + if (.not. exists(global_settings%path_to_config_folder) & + .or. .not. exists(global_settings%full_path())) return - ! Deallocate name and return if the config file doesn't exist. - if (.not. exists(global_settings%full_path())) then - deallocate (global_settings%file_name) - return - end if end if ! Load into TOML table. @@ -101,6 +91,7 @@ subroutine get_global_settings(global_settings, error) end subroutine get_global_settings + !> Get settings from the [registry] table in the global config file. subroutine get_registry_settings(global_settings, table, error) type(fpm_global_settings), intent(inout) :: global_settings type(toml_table), intent(inout) :: table @@ -131,14 +122,13 @@ subroutine get_registry_settings(global_settings, table, error) if (allocated(path)) then ! Relative path, join path to the global config file with the path to the registry. - call get_absolute_path(join_path(global_settings%path_to_folder, path), & + call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & global_settings%registry_settings%path, error) if (allocated(error)) return ! Check if the new path to the registry exists. if (.not. exists(global_settings%registry_settings%path)) then call fatal_error(error, "No registry at: '"//global_settings%registry_settings%path//"'") - deallocate (global_settings%registry_settings%path) return end if @@ -163,12 +153,19 @@ subroutine get_registry_settings(global_settings, table, error) end subroutine get_registry_settings + !> True if the global config file is not at the default location. + pure logical function has_custom_location(self) + class(fpm_global_settings), intent(in) :: self + + has_custom_location = allocated(self%path_to_config_folder) .and. allocated(self%config_file_name) + end function + !> The full path to the global config file. function full_path(self) result(result) class(fpm_global_settings), intent(in) :: self character(len=:), allocatable :: result - result = join_path(self%path_to_folder, self%file_name) + result = join_path(self%path_to_config_folder, self%config_file_name) end function !> The official registry is used by default when no local or custom registry was specified. From 491e801deb84dd3f7e1a81b8f25fcf44582757a9 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 27 Jan 2023 19:00:46 +0100 Subject: [PATCH 067/799] Eliminate variable --- src/fpm/dependency.f90 | 12 ++++++++++++ src/fpm_os.F90 | 15 +++++++++++++-- src/fpm_settings.f90 | 8 ++------ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index aafa0c7a9c..b1ac71d24c 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -66,6 +66,7 @@ module fpm_dependency use fpm_toml, only : toml_table, toml_key, toml_error, toml_serializer, & toml_parse, get_value, set_value, add_table use fpm_versioning, only : version_t, new_version, char + use fpm_settings, only: fpm_global_settings, get_global_settings implicit none private @@ -448,6 +449,7 @@ subroutine resolve_dependency(self, dependency, root, error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package + type(fpm_global_settings) :: global_settings character(len=:), allocatable :: manifest, proj_dir, revision logical :: fetch @@ -464,6 +466,16 @@ subroutine resolve_dependency(self, dependency, root, error) call dependency%git%checkout(proj_dir, error) if (allocated(error)) return end if + else + call get_global_settings(global_settings, error) + ! proj_dir = global_settings%registry%path + ! Get global settings + ! Path is defined in config.toml + ! Make sure local registry (folder) exists + ! Make sure it has namespace folders + ! Make directories for dependencies + ! proj_dir = is the goal + ! Record added date and throw out oldest one if we are exceeding limit end if if (allocated(dependency%git)) then diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 86a1625235..7d37801837 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -5,7 +5,7 @@ module fpm_os use fpm_error, only: error_t, fatal_error implicit none private - public :: change_directory, get_current_directory, get_absolute_path + public :: change_directory, get_current_directory, get_absolute_path, convert_to_absolute_path integer(c_int), parameter :: buffersize = 1000_c_int @@ -161,7 +161,7 @@ subroutine get_realpath(path, real_path, error) end subroutine get_realpath !> Determine the canonical, absolute path for the given path. - !> It contains expansion of the home folder (~). + !> Expands home folder (~) on both Unix and Windows. subroutine get_absolute_path(path, absolute_path, error) character(len=*), intent(in) :: path character(len=:), allocatable, intent(out) :: absolute_path @@ -214,4 +214,15 @@ subroutine get_absolute_path(path, absolute_path, error) end subroutine + !> Converts a path to an absolute, canonical path. + subroutine convert_to_absolute_path(path, error) + character(len=*), intent(inout) :: path + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: absolute_path + + call get_absolute_path(path, absolute_path, error) + path = absolute_path + end subroutine + end module fpm_os diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 757f32bcb1..7df6871f5f 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -4,7 +4,7 @@ module fpm_settings use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value - use fpm_os, only: get_current_directory, change_directory, get_absolute_path + use fpm_os, only: get_current_directory, change_directory, get_absolute_path, convert_to_absolute_path use tomlf, only: toml_load implicit none private @@ -34,8 +34,6 @@ subroutine get_global_settings(global_settings, error) type(fpm_global_settings), intent(inout) :: global_settings !> Error reading config file. type(error_t), allocatable, intent(out) :: error - !> Absolute path to the config file. - character(len=:), allocatable :: abs_path_to_config !> TOML table to be filled with global config settings. type(toml_table), allocatable :: table !> Error parsing to TOML table. @@ -56,10 +54,8 @@ subroutine get_global_settings(global_settings, error) end if ! Make sure that the path to the global config file is absolute. - call get_absolute_path(global_settings%path_to_config_folder, abs_path_to_config, error) + call convert_to_absolute_path(global_settings%path_to_config_folder, error) if (allocated(error)) return - - global_settings%path_to_config_folder = abs_path_to_config else ! Use default path if it wasn't specified. if (os_is_unix()) then From 7a4e530e3f8c68a1fd970f6bef980f5d314244c6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 28 Jan 2023 18:07:17 +0100 Subject: [PATCH 068/799] Fix absolute paths to local registry --- src/fpm_settings.f90 | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 7df6871f5f..32ad4d3975 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,10 +1,11 @@ !> Manages global settings which are defined in the global config file. module fpm_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix + use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value - use fpm_os, only: get_current_directory, change_directory, get_absolute_path, convert_to_absolute_path + use fpm_os, only: get_current_directory, change_directory, get_absolute_path, & + convert_to_absolute_path use tomlf, only: toml_load implicit none private @@ -117,17 +118,20 @@ subroutine get_registry_settings(global_settings, table, error) end if if (allocated(path)) then - ! Relative path, join path to the global config file with the path to the registry. - call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & - global_settings%registry_settings%path, error) - if (allocated(error)) return - - ! Check if the new path to the registry exists. - if (.not. exists(global_settings%registry_settings%path)) then - call fatal_error(error, "No registry at: '"//global_settings%registry_settings%path//"'") - return + if (is_absolute_path(path)) then + global_settings%registry_settings%path = path + else + ! Get canonical, absolute path on both Unix and Windows. + call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & + global_settings%registry_settings%path, error) + if (allocated(error)) return + + ! Check if the path to the registry exists. + if (.not. exists(global_settings%registry_settings%path)) then + call fatal_error(error, "No registry at: '"//global_settings%registry_settings%path//"'") + return + end if end if - end if call get_value(child, 'url', url, stat=stat) From e5af3c9c10c4c2eb9ddb539dbd59069ca91e4ee7 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 29 Jan 2023 01:32:04 +0100 Subject: [PATCH 069/799] Add whitespace to error messages --- src/fpm.f90 | 12 ++++++------ src/fpm/manifest/dependency.f90 | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index cc41299d53..7bc865fe56 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -292,17 +292,17 @@ subroutine cmd_build(settings) call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) if (allocated(error)) then - call fpm_stop(1,'*cmd_build*:package error:'//error%message) + call fpm_stop(1,'*cmd_build* Package error: '//error%message) end if call build_model(model, settings, package, error) if (allocated(error)) then - call fpm_stop(1,'*cmd_build*:model error:'//error%message) + call fpm_stop(1,'*cmd_build* Model error: '//error%message) end if call targets_from_sources(targets, model, settings%prune, error) if (allocated(error)) then - call fpm_stop(1,'*cmd_build*:target error:'//error%message) + call fpm_stop(1,'*cmd_build* Target error: '//error%message) end if if(settings%list)then @@ -338,17 +338,17 @@ subroutine cmd_run(settings,test) call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) if (allocated(error)) then - call fpm_stop(1, '*cmd_run*:package error:'//error%message) + call fpm_stop(1, '*cmd_run* Package error: '//error%message) end if call build_model(model, settings%fpm_build_settings, package, error) if (allocated(error)) then - call fpm_stop(1, '*cmd_run*:model error:'//error%message) + call fpm_stop(1, '*cmd_run* Model error: '//error%message) end if call targets_from_sources(targets, model, settings%prune, error) if (allocated(error)) then - call fpm_stop(1, '*cmd_run*:targets error:'//error%message) + call fpm_stop(1, '*cmd_run* Targets error: '//error%message) end if if (test) then diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index efed2041ee..3e8648912d 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -174,7 +174,7 @@ subroutine check(table, error) return end if - ! Check if value can be mapped or else show error message with the error location + ! Check if value can be mapped or else (wrong type) show error message with the error location call get_value(table, list(ikey)%key, value) if (.not. allocated(value)) then call syntax_error(error, "Dependency '"//name//"' has invalid '"//list(ikey)%key//"' entry") From f71e38b03a9b2c7bf4a8836e54d0f7819e4284f5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 29 Jan 2023 01:51:53 +0100 Subject: [PATCH 070/799] Only use function in versioning --- src/fpm.f90 | 5 ++--- src/fpm/versioning.f90 | 28 +++++----------------------- test/fpm_test/test_manifest.f90 | 14 +++----------- test/fpm_test/test_settings.f90 | 4 ++-- test/fpm_test/test_versioning.f90 | 6 ++---- 5 files changed, 14 insertions(+), 43 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 7bc865fe56..6af602aedd 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -40,7 +40,7 @@ subroutine build_model(model, settings, package, error) integer :: i, j type(package_config_t) :: dependency - character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags, version + character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags logical :: has_cpp logical :: duplicates_found = .false. type(string_t) :: include_dir @@ -100,8 +100,7 @@ subroutine build_model(model, settings, package, error) if (allocated(error)) exit model%packages(i)%name = dependency%name - call package%version%to_string(version) - model%packages(i)%version = version + model%packages(i)%version = package%version%s() if (allocated(dependency%preprocess)) then do j = 1, size(dependency%preprocess) diff --git a/src/fpm/versioning.f90 b/src/fpm/versioning.f90 index da362eeee4..4007fdf55d 100644 --- a/src/fpm/versioning.f90 +++ b/src/fpm/versioning.f90 @@ -4,7 +4,7 @@ module fpm_versioning implicit none private - public :: version_t, new_version, char + public :: version_t, new_version type :: version_t @@ -38,7 +38,7 @@ module fpm_versioning procedure, private :: match !> Create a printable string from a version data type - procedure :: to_string + procedure :: s end type version_t @@ -47,11 +47,6 @@ module fpm_versioning integer, parameter :: max_limit = 3 - interface char - module procedure :: as_string - end interface char - - interface new_version module procedure :: new_version_from_string module procedure :: new_version_from_int @@ -220,13 +215,13 @@ subroutine token_error(error, string, istart, iend, message) end subroutine token_error - subroutine to_string(self, string) + pure function s(self) result(string) !> Version number class(version_t), intent(in) :: self !> Character representation of the version - character(len=:), allocatable, intent(out) :: string + character(len=:), allocatable :: string integer, parameter :: buffersize = 64 character(len=buffersize) :: buffer @@ -246,20 +241,7 @@ subroutine to_string(self, string) string = '0' end if - end subroutine to_string - - - function as_string(self) result(string) - - !> Version number - class(version_t), intent(in) :: self - - !> Character representation of the version - character(len=:), allocatable :: string - - call self%to_string(string) - - end function as_string + end function s !> Check to version numbers for equality diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index a58fbfb193..b870dc38be 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1320,8 +1320,6 @@ subroutine test_macro_parsing(error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: version - type(package_config_t) :: package character(:), allocatable :: temp_file integer :: unit @@ -1342,9 +1340,7 @@ subroutine test_macro_parsing(error) if (allocated(error)) return - call package%version%to_string(version) - - if (get_macros(id, package%preprocess(1)%macros, version) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then + if (get_macros(id, package%preprocess(1)%macros, package%version%s()) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then call test_failed(error, "Macros were not parsed correctly") end if @@ -1358,7 +1354,6 @@ subroutine test_macro_parsing_dependency(error) type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: macrosPackage, macrosDependency - character(len=:), allocatable :: versionPackage, versionDependency type(package_config_t) :: package, dependency @@ -1400,11 +1395,8 @@ subroutine test_macro_parsing_dependency(error) if (allocated(error)) return - call package%version%to_string(versionPackage) - call dependency%version%to_string(versionDependency) - - macrosPackage = get_macros(id, package%preprocess(1)%macros, versionPackage) - macrosDependency = get_macros(id, dependency%preprocess(1)%macros, versionDependency) + macrosPackage = get_macros(id, package%preprocess(1)%macros, package%version%s()) + macrosDependency = get_macros(id, dependency%preprocess(1)%macros, dependency%version%s()) if (macrosPackage == macrosDependency) then call test_failed(error, "Macros of package and dependency should not be equal") diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 3c60470768..1ab6043c08 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -45,8 +45,8 @@ subroutine setup_global_settings(global_settings) type(fpm_global_settings), allocatable, intent(out) :: global_settings allocate (global_settings) - global_settings%path_to_folder = tmp_folder - global_settings%file_name = config_file_name + global_settings%path_to_config_folder = tmp_folder + global_settings%config_file_name = config_file_name end subroutine !> Throw error when custom path to config file was entered but no folder exists. diff --git a/test/fpm_test/test_versioning.f90 b/test/fpm_test/test_versioning.f90 index b309d1382c..c24149185a 100644 --- a/test/fpm_test/test_versioning.f90 +++ b/test/fpm_test/test_versioning.f90 @@ -322,15 +322,13 @@ subroutine test_valid_string(error) type(error_t), allocatable, intent(out) :: error character(len=*), parameter :: str_in = "20.1.100" - character(len=:), allocatable :: str_out type(version_t) :: version call new_version(version, str_in, error) if (allocated(error)) return - call version%to_string(str_out) - if (str_in /= str_out) then - call test_failed(error, "Expected "//str_in//" but got "//str_out) + if (str_in /= version%s()) then + call test_failed(error, "Expected "//str_in//" but got "//version%s()) end if end subroutine test_valid_string From 81307bf0c555dd975678ed7af049128b35d2925b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 29 Jan 2023 01:55:28 +0100 Subject: [PATCH 071/799] Change in fpm_dependency, too --- src/fpm/dependency.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index b1ac71d24c..1a4ac20288 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -65,7 +65,7 @@ module fpm_dependency use fpm_strings, only : string_t, operator(.in.) use fpm_toml, only : toml_table, toml_key, toml_error, toml_serializer, & toml_parse, get_value, set_value, add_table - use fpm_versioning, only : version_t, new_version, char + use fpm_versioning, only : version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings implicit none private @@ -492,7 +492,7 @@ subroutine resolve_dependency(self, dependency, root, error) if (self%verbosity > 1) then write(self%unit, out_fmt) & - "Dep:", dependency%name, "version", char(dependency%version), & + "Dep:", dependency%name, "version", dependency%version%s(), & "at", dependency%proj_dir end if @@ -761,7 +761,7 @@ subroutine dump_to_toml(self, table, error) exit end if if (allocated(dep%version)) then - call set_value(ptr, "version", char(dep%version)) + call set_value(ptr, "version", dep%version%s()) end if proj_dir = canon_path(dep%proj_dir) call set_value(ptr, "proj-dir", proj_dir) From 366fbd43bae2f3f8c488fee9fc1b6f41126d428a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 29 Jan 2023 02:35:05 +0100 Subject: [PATCH 072/799] Get dependencies from local registry (cache) with simple versioning --- src/fpm/dependency.f90 | 136 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 125 insertions(+), 11 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 1a4ac20288..b1e066aa4d 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -58,7 +58,7 @@ module fpm_dependency use, intrinsic :: iso_fortran_env, only : output_unit use fpm_environment, only : get_os_type, OS_WINDOWS use fpm_error, only : error_t, fatal_error - use fpm_filesystem, only : exists, join_path, mkdir, canon_path, windows_path + use fpm_filesystem, only : exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename use fpm_git, only : git_target_revision, git_target_default, git_revision use fpm_manifest, only : package_config_t, dependency_config_t, & get_package_data @@ -449,7 +449,6 @@ subroutine resolve_dependency(self, dependency, root, error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package - type(fpm_global_settings) :: global_settings character(len=:), allocatable :: manifest, proj_dir, revision logical :: fetch @@ -467,15 +466,8 @@ subroutine resolve_dependency(self, dependency, root, error) if (allocated(error)) return end if else - call get_global_settings(global_settings, error) - ! proj_dir = global_settings%registry%path - ! Get global settings - ! Path is defined in config.toml - ! Make sure local registry (folder) exists - ! Make sure it has namespace folders - ! Make directories for dependencies - ! proj_dir = is the goal - ! Record added date and throw out oldest one if we are exceeding limit + call get_from_registry(dependency, proj_dir, error) + if (allocated(error)) return end if if (allocated(dependency%git)) then @@ -501,6 +493,128 @@ subroutine resolve_dependency(self, dependency, root, error) end subroutine resolve_dependency + !> Get a dependency from a registry. It can be local, a custom url registry + !> or the default registry. + subroutine get_from_registry(dep, target_dir, error) + + !> Instance of the dependency configuration. + class(dependency_config_t), intent(in) :: dep + + !> The target directory of the dependency. + character(:), allocatable, intent(out) :: target_dir + + !> Error handling. + type(error_t), allocatable, intent(out) :: error + + type(fpm_global_settings) :: global_settings + + call get_global_settings(global_settings, error) + if (allocated(error)) return + ! Registry settings found in the global config file. + if (allocated(global_settings%registry_settings)) then + ! A Path to the local registry was specified. + if (allocated(global_settings%registry_settings%path)) then + ! The local registry now acts as the cache. + call get_from_registry_cache(dep, target_dir, global_settings%registry_settings%path, error) + if (allocated(error)) return + ! Use the registry from a custom url. + else if (allocated(global_settings%registry_settings%url)) then + ! Collect existing versions from the cache. + ! Get new versions from the registry, sending existing versions. + ! Put them in the cache, build cache if necessary. + ! Use default location for the cache. + call get_from_registry_cache(dep, target_dir,& + join_path(global_settings%path_to_config_folder, 'dependencies'), error) + if (allocated(error)) return + end if + else + ! Collect existing versions from the cache. + ! Get new versions from the registry, sending existing versions. + ! Put them in the cache, build cache if necessary. + ! Use default location for the cache. + call get_from_registry_cache(dep, target_dir,& + join_path(global_settings%path_to_config_folder, 'dependencies'), error) + if (allocated(error)) return + end if + + end subroutine get_from_registry + + !> Get the dependency from the registry cache. + subroutine get_from_registry_cache(dep, target_dir, cache_path, error) + + !> Instance of the dependency configuration. + class(dependency_config_t), intent(in) :: dep + + !> The target directory to download the dependency to. + character(:), allocatable, intent(out) :: target_dir + + !> The path to the registry cache. + character(*), intent(in) :: cache_path + + !> Error handling. + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: path_to_name + type(string_t), allocatable :: files(:) + type(version_t), allocatable :: versions(:) + type(version_t) :: version + integer :: i + + path_to_name = join_path(cache_path, dep%namespace, dep%name) + + if (.not. exists(path_to_name)) then + call fatal_error(error, "Dependency '"//dep%name//"' not found in path '"//path_to_name//"'") + return + end if + + ! Returns absolute paths. + call list_files(path_to_name, files) + if (size(files) == 0) then + call fatal_error(error, "No dependencies found in '"//path_to_name//"'") + return + end if + + ! Version requested, find it in the cache. + if (allocated(dep%vers)) then + do i = 1, size(files) + ! Identify directory that matches the version number. + if (files(i)%s == join_path(path_to_name, dep%vers)) then + if (.not. is_dir(files(i)%s)) then + call fatal_error(error, "'"//files(i)%s//"' is not a directory") + return + end if + target_dir = files(i)%s + return + end if + end do + call fatal_error(error, "Version '"//dep%vers//"' not found in '"//path_to_name//"'") + return + end if + + ! No version requested, get the latest version. + do i = 1, size(files) + if (is_dir(files(i)%s)) then + ! Generate list of versions for semantic versioning. + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + versions = [versions, version] + end if + end do + + if (size(versions) == 0) then + call fatal_error(error, "No versions found in '"//path_to_name//"'") + return + end if + + ! Get the latest version. + version = versions(1) + do i = 1, size(versions) + if (versions(i) > version) version = versions(i) + end do + + target_dir = join_path(path_to_name, version%s()) + end subroutine get_from_registry_cache + !> True if dependency is part of the tree pure logical function has_dependency(self, dependency) !> Instance of the dependency tree From fa4b180968db2139ed3337e524b29007955cad97 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 29 Jan 2023 15:07:25 +0100 Subject: [PATCH 073/799] Nit and not throw when not a directory --- src/fpm/dependency.f90 | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index b1e066aa4d..9184ea55d0 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -567,7 +567,6 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) return end if - ! Returns absolute paths. call list_files(path_to_name, files) if (size(files) == 0) then call fatal_error(error, "No dependencies found in '"//path_to_name//"'") @@ -578,11 +577,7 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) if (allocated(dep%vers)) then do i = 1, size(files) ! Identify directory that matches the version number. - if (files(i)%s == join_path(path_to_name, dep%vers)) then - if (.not. is_dir(files(i)%s)) then - call fatal_error(error, "'"//files(i)%s//"' is not a directory") - return - end if + if (files(i)%s == join_path(path_to_name, dep%vers) .and. is_dir(files(i)%s)) then target_dir = files(i)%s return end if @@ -591,10 +586,9 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) return end if - ! No version requested, get the latest version. + ! No version requested, generate list of available versions. do i = 1, size(files) if (is_dir(files(i)%s)) then - ! Generate list of versions for semantic versioning. call new_version(version, basename(files(i)%s), error) if (allocated(error)) return versions = [versions, version] @@ -606,7 +600,7 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) return end if - ! Get the latest version. + ! Find the latest version. version = versions(1) do i = 1, size(versions) if (versions(i) > version) version = versions(i) From 3e8634a7716c4c152da5583b01d38bf12f570cbd Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 30 Jan 2023 16:51:05 +0100 Subject: [PATCH 074/799] Optinally match version dir name with package version --- src/fpm/dependency.f90 | 43 ++++++++++++++++++++++++++++++++++++++++-- src/fpm/toml.f90 | 9 ++++----- src/fpm_settings.f90 | 3 +-- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 9184ea55d0..08a2122ca9 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -64,7 +64,7 @@ module fpm_dependency get_package_data use fpm_strings, only : string_t, operator(.in.) use fpm_toml, only : toml_table, toml_key, toml_error, toml_serializer, & - toml_parse, get_value, set_value, add_table + toml_parse, get_value, set_value, add_table, toml_load, toml_stat use fpm_versioning, only : version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings implicit none @@ -607,7 +607,46 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) end do target_dir = join_path(path_to_name, version%s()) - end subroutine get_from_registry_cache + end subroutine get_from_registry_cache + + !> Checks if the directory name matches the package version. + subroutine check_version(dir_path, error) + + !> Absolute path to the package-containing directory. + character(*), intent(in) :: dir_path + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), allocatable :: table + type(toml_error), allocatable :: parse_error + integer :: stat + character(:), allocatable :: version + + call toml_load(table, join_path(dir_path, 'fpm.toml'), error=parse_error) + + if (allocated(parse_error)) then + allocate (error) + call move_alloc(parse_error%message, error%message) + return + end if + + call get_value(table, 'version', version, stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error reading version number from "' & + //join_path(dir_path, 'fpm.toml')//'"') + return + end if + + if (version /= basename(dir_path)) then + call fatal_error(error, "Directory name '"//basename(dir_path) & + //"' does not match version number '"//version//" ' in package '"// & + dir_path//"'") + return + end if + + end subroutine check_version !> True if dependency is part of the tree pure logical function has_dependency(self, dependency) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 3b05229e66..9d71474b6b 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -17,14 +17,13 @@ module fpm_toml use fpm_strings, only : string_t use tomlf, only : toml_table, toml_array, toml_key, toml_stat, get_value, & & set_value, toml_parse, toml_error, new_table, add_table, add_array, & - & toml_serializer, len + & toml_serializer, len, toml_load implicit none private - public :: read_package_file - public :: toml_table, toml_array, toml_key, toml_stat, get_value, set_value, get_list - public :: new_table, add_table, add_array, len - public :: toml_error, toml_serializer, toml_parse + public :: read_package_file, toml_table, toml_array, toml_key, toml_stat, & + get_value, set_value, get_list, new_table, add_table, add_array, len, & + toml_error, toml_serializer, toml_parse, toml_load contains diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 32ad4d3975..18a037b490 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -3,10 +3,9 @@ module fpm_settings use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error - use fpm_toml, only: toml_table, toml_error, toml_stat, get_value + use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load use fpm_os, only: get_current_directory, change_directory, get_absolute_path, & convert_to_absolute_path - use tomlf, only: toml_load implicit none private public :: fpm_global_settings, get_global_settings From aab722cdc46d5abecc4dcaa98655e3d47c4bb70f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 31 Jan 2023 11:40:29 +0100 Subject: [PATCH 075/799] Clean up get_from_registry --- src/fpm/dependency.f90 | 53 ++++++++++++++++++++++++------------------ src/fpm_settings.f90 | 1 + 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 08a2122ca9..bd874ac9b4 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -510,36 +510,22 @@ subroutine get_from_registry(dep, target_dir, error) call get_global_settings(global_settings, error) if (allocated(error)) return + ! Registry settings found in the global config file. if (allocated(global_settings%registry_settings)) then - ! A Path to the local registry was specified. - if (allocated(global_settings%registry_settings%path)) then - ! The local registry now acts as the cache. - call get_from_registry_cache(dep, target_dir, global_settings%registry_settings%path, error) - if (allocated(error)) return - ! Use the registry from a custom url. - else if (allocated(global_settings%registry_settings%url)) then - ! Collect existing versions from the cache. - ! Get new versions from the registry, sending existing versions. - ! Put them in the cache, build cache if necessary. - ! Use default location for the cache. - call get_from_registry_cache(dep, target_dir,& - join_path(global_settings%path_to_config_folder, 'dependencies'), error) - if (allocated(error)) return - end if - else - ! Collect existing versions from the cache. - ! Get new versions from the registry, sending existing versions. - ! Put them in the cache, build cache if necessary. - ! Use default location for the cache. - call get_from_registry_cache(dep, target_dir,& - join_path(global_settings%path_to_config_folder, 'dependencies'), error) - if (allocated(error)) return + if (allocated(global_settings%registry_settings%path)) then + ! The registry cache acts as the local registry. + call get_from_registry_cache(dep, target_dir, global_settings%registry_settings%path, error) + return + end if end if + call get_from_registry_url(dep, target_dir, global_settings, error) + end subroutine get_from_registry !> Get the dependency from the registry cache. + !> Throw error if the package isn't found. subroutine get_from_registry_cache(dep, target_dir, cache_path, error) !> Instance of the dependency configuration. @@ -648,6 +634,27 @@ subroutine check_version(dir_path, error) end subroutine check_version + !> Get dependency from a registry via url. + subroutine get_from_registry_url(dep, target_dir, global_settings, error) + + !> Instance of the dependency configuration. + class(dependency_config_t), intent(in) :: dep + + !> The target directory to download the dependency to. + character(:), allocatable, intent(out) :: target_dir + + !> Global config settings. + type(fpm_global_settings), intent(in) :: global_settings + + !> Error handling. + type(error_t), allocatable, intent(out) :: error + + ! Collect existing versions from the cache. + ! Get new versions from the registry, sending existing versions. + ! Put them in the cache, build cache if necessary. + ! Use default location for the cache. + end subroutine get_from_registry_url + !> True if dependency is part of the tree pure logical function has_dependency(self, dependency) !> Instance of the dependency tree diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 18a037b490..b2eec5d257 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -15,6 +15,7 @@ module fpm_settings character(len=:), allocatable :: path_to_config_folder !> Name of the global config file. The default is `config.toml`. character(len=:), allocatable :: config_file_name + !> Registry configs. type(fpm_registry_settings), allocatable :: registry_settings contains procedure :: has_custom_location, full_path From bf8ab3a429e827b048161f7634a3566831bc3d77 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 31 Jan 2023 14:40:15 +0100 Subject: [PATCH 076/799] Read cache directory from registry settings --- src/fpm_settings.f90 | 42 +++++++++++++++++++++++++++++---- test/fpm_test/test_settings.f90 | 31 ++++++++++++------------ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index b2eec5d257..7a6e2a8d1c 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -22,8 +22,18 @@ module fpm_settings end type type :: fpm_registry_settings + !> The path to the local registry. If allocated, the local registry + !> will be used instead of the remote registry and replaces the + !> local cache. character(len=:), allocatable :: path + !> The URL to the remote registry. Can be used to get packages + !> from the official or a custom registry. character(len=:), allocatable :: url + !> The path to the cache folder. If not specified, the default cache + !> folders are `~/.local/share/fpm/dependencies` on Unix and + !> `%APPDATA%\local\fpm\dependencies` on Windows. + !> Cannot be used together with `path`. + character(len=:), allocatable :: cache_path contains procedure :: uses_default_registry end type @@ -94,7 +104,7 @@ subroutine get_registry_settings(global_settings, table, error) type(toml_table), intent(inout) :: table type(error_t), allocatable, intent(out) :: error type(toml_table), pointer :: child - character(:), allocatable :: path, url + character(:), allocatable :: path, url, cache_path integer :: stat call get_value(table, 'registry', child, requested=.false., stat=stat) @@ -113,7 +123,7 @@ subroutine get_registry_settings(global_settings, table, error) call get_value(child, 'path', path, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, 'Error parsing path to registry: "'//path//'"') + call fatal_error(error, 'Error reading registry path: "'//path//'"') return end if @@ -128,7 +138,8 @@ subroutine get_registry_settings(global_settings, table, error) ! Check if the path to the registry exists. if (.not. exists(global_settings%registry_settings%path)) then - call fatal_error(error, "No registry at: '"//global_settings%registry_settings%path//"'") + call fatal_error(error, "Directory '"//global_settings%registry_settings%path// & + "' does not exist") return end if end if @@ -137,7 +148,7 @@ subroutine get_registry_settings(global_settings, table, error) call get_value(child, 'url', url, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, 'Error parsing url to registry: "'//url//'"') + call fatal_error(error, 'Error reading registry url: "'//url//'"') return end if @@ -151,6 +162,29 @@ subroutine get_registry_settings(global_settings, table, error) global_settings%registry_settings%url = url end if + call get_value(child, 'cache_path', cache_path, stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error reading path to registry cache: "'//cache_path//'"') + return + end if + + if (allocated(cache_path)) then + ! Throw error when both path and cache_path were provided. + if (allocated(path)) then + call fatal_error(error, 'Do not provide both path and cache_path') + return + end if + + if (is_absolute_path(cache_path)) then + global_settings%registry_settings%cache_path = cache_path + else + ! Get canonical, absolute path on both Unix and Windows. + call get_absolute_path(join_path(global_settings%path_to_config_folder, cache_path), & + global_settings%registry_settings%cache_path, error) + if (allocated(error)) return + end if + end if end subroutine get_registry_settings !> True if the global config file is not at the default location. diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 1ab6043c08..48b710b2f5 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -42,9 +42,8 @@ subroutine delete_tmp_folder end subroutine setup_global_settings(global_settings) - type(fpm_global_settings), allocatable, intent(out) :: global_settings + type(fpm_global_settings), intent(out) :: global_settings - allocate (global_settings) global_settings%path_to_config_folder = tmp_folder global_settings%config_file_name = config_file_name end subroutine @@ -52,9 +51,9 @@ subroutine setup_global_settings(global_settings) !> Throw error when custom path to config file was entered but no folder exists. subroutine no_folder(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call setup_global_settings(global_settings) call get_global_settings(global_settings, error) end subroutine no_folder @@ -62,9 +61,9 @@ end subroutine no_folder !> Throw error when custom path to config file was entered but no file exists. subroutine no_file(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call setup_global_settings(global_settings) call get_global_settings(global_settings, error) @@ -73,9 +72,9 @@ end subroutine no_file !> Config file exists and working directory is set. subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), ['']) @@ -95,7 +94,7 @@ end subroutine empty_file subroutine empty_registry_table(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings call delete_tmp_folder() call mkdir(tmp_folder) @@ -127,7 +126,7 @@ subroutine empty_registry_table(error) subroutine has_non_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings call delete_tmp_folder() call mkdir(tmp_folder) @@ -141,7 +140,7 @@ subroutine has_non_existent_path_to_registry(error) subroutine has_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings call delete_tmp_folder() call mkdir(tmp_folder) @@ -170,7 +169,7 @@ subroutine has_existent_path_to_registry(error) subroutine absolute_path_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path call delete_tmp_folder() @@ -208,7 +207,7 @@ subroutine absolute_path_to_registry(error) subroutine relative_path_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path call delete_tmp_folder() @@ -238,7 +237,7 @@ subroutine relative_path_to_registry(error) subroutine canonical_path_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path call delete_tmp_folder() @@ -269,7 +268,7 @@ subroutine canonical_path_to_registry(error) subroutine has_url_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings call delete_tmp_folder() call mkdir(tmp_folder) @@ -300,7 +299,7 @@ subroutine has_url_to_registry(error) subroutine has_both_path_and_url_to_registry(error) type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings), allocatable :: global_settings + type(fpm_global_settings) :: global_settings call delete_tmp_folder() call mkdir(tmp_folder) From 3f53306fb4ead8c96b29b2339900309e403d2404 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 31 Jan 2023 15:26:26 +0100 Subject: [PATCH 077/799] Create cache if it does not exist --- src/fpm_os.F90 | 2 +- src/fpm_settings.f90 | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 7d37801837..a59586d0ad 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -136,7 +136,7 @@ subroutine get_realpath(path, real_path, error) type(c_ptr) :: ptr if (.not. exists(path)) then - call fatal_error(error, "Path '"//path//"' does not exist") + call fatal_error(error, "Cannot get real path. Path '"//path//"' does not exist") return end if diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 7a6e2a8d1c..af88d42cde 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,6 +1,6 @@ !> Manages global settings which are defined in the global config file. module fpm_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path + use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path, mkdir use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load @@ -172,13 +172,17 @@ subroutine get_registry_settings(global_settings, table, error) if (allocated(cache_path)) then ! Throw error when both path and cache_path were provided. if (allocated(path)) then - call fatal_error(error, 'Do not provide both path and cache_path') + call fatal_error(error, "Do not provide both 'path' and 'cache_path'") return end if if (is_absolute_path(cache_path)) then + if (.not. exists(cache_path)) call mkdir(cache_path) global_settings%registry_settings%cache_path = cache_path else + if (.not. exists(join_path(global_settings%path_to_config_folder, cache_path))) then + call mkdir(join_path(global_settings%path_to_config_folder, cache_path)) + end if ! Get canonical, absolute path on both Unix and Windows. call get_absolute_path(join_path(global_settings%path_to_config_folder, cache_path), & global_settings%registry_settings%cache_path, error) From d6cc28d9f8200803ca5e6822c7384dd91e97e50e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 31 Jan 2023 21:26:12 +0100 Subject: [PATCH 078/799] Build cache if not present, remove unnecessary function, collect verions, map version, not string --- src/fpm/dependency.f90 | 27 +++++++++++++++++++++------ src/fpm/manifest/dependency.f90 | 12 +++++++++--- src/fpm_settings.f90 | 15 +++++---------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index bd874ac9b4..70bf567b7f 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -493,8 +493,8 @@ subroutine resolve_dependency(self, dependency, root, error) end subroutine resolve_dependency - !> Get a dependency from a registry. It can be local, a custom url registry - !> or the default registry. + !> Get a dependency from the registry. The registry can be local, custom + !> (reached via url) or the official one. subroutine get_from_registry(dep, target_dir, error) !> Instance of the dependency configuration. @@ -563,12 +563,12 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) if (allocated(dep%vers)) then do i = 1, size(files) ! Identify directory that matches the version number. - if (files(i)%s == join_path(path_to_name, dep%vers) .and. is_dir(files(i)%s)) then + if (files(i)%s == join_path(path_to_name, dep%vers%s()) .and. is_dir(files(i)%s)) then target_dir = files(i)%s return end if end do - call fatal_error(error, "Version '"//dep%vers//"' not found in '"//path_to_name//"'") + call fatal_error(error, "Version '"//dep%vers%s()//"' not found in '"//path_to_name//"'") return end if @@ -649,10 +649,25 @@ subroutine get_from_registry_url(dep, target_dir, global_settings, error) !> Error handling. type(error_t), allocatable, intent(out) :: error + type(string_t), allocatable :: files(:) + type(version_t), allocatable :: versions(:), version + integer :: i + ! Collect existing versions from the cache. + call list_files(global_settings%registry_settings%cache_path, files) + + if (size(files) > 0) then + do i = 1, size(files) + if (.not. is_dir(files(i)%s)) cycle + + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + + versions = [versions, version] + end do + end if ! Get new versions from the registry, sending existing versions. - ! Put them in the cache, build cache if necessary. - ! Use default location for the cache. + ! Put them in the cache. end subroutine get_from_registry_url !> True if dependency is part of the tree diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 3e8648912d..a57030b6f5 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -29,6 +29,7 @@ module fpm_manifest_dependency use fpm_toml, only : toml_table, toml_key, toml_stat, get_value use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS + use fpm_versioning, only : version_t, new_version implicit none private @@ -51,7 +52,7 @@ module fpm_manifest_dependency !> The specified version of the dependency. !> The latest version is used if not specified. - character(len=:), allocatable :: vers + type(version_t), allocatable :: vers !> Git descriptor type(git_target_t), allocatable :: git @@ -82,7 +83,7 @@ subroutine new_dependency(self, table, root, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: uri, value + character(len=:), allocatable :: uri, value, version call check(table, error) if (allocated(error)) return @@ -125,7 +126,12 @@ subroutine new_dependency(self, table, root, error) return end if - call get_value(table, "vers", self%vers) + call get_value(table, 'vers', version) + + if (allocated(version)) then + call new_version(self%vers, version, error) + if (allocated(error)) return + end if end subroutine new_dependency diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index af88d42cde..2234a081af 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -34,8 +34,6 @@ module fpm_settings !> `%APPDATA%\local\fpm\dependencies` on Windows. !> Cannot be used together with `path`. character(len=:), allocatable :: cache_path - contains - procedure :: uses_default_registry end type contains @@ -81,7 +79,6 @@ subroutine get_global_settings(global_settings, error) ! Return if path or file doesn't exist. if (.not. exists(global_settings%path_to_config_folder) & .or. .not. exists(global_settings%full_path())) return - end if ! Load into TOML table. @@ -188,6 +185,11 @@ subroutine get_registry_settings(global_settings, table, error) global_settings%registry_settings%cache_path, error) if (allocated(error)) return end if + ! Both path and cache_path not allocated, use default location for cache_path. + else if (.not. allocated(path)) then + cache_path = join_path(global_settings%path_to_config_folder, 'dependencies') + global_settings%registry_settings%cache_path = cache_path + if (.not. exists(cache_path)) call mkdir(cache_path) end if end subroutine get_registry_settings @@ -206,11 +208,4 @@ function full_path(self) result(result) result = join_path(self%path_to_config_folder, self%config_file_name) end function - !> The official registry is used by default when no local or custom registry was specified. - pure logical function uses_default_registry(self) - class(fpm_registry_settings), intent(in) :: self - - uses_default_registry = .not. allocated(self%path) .and. .not. allocated(self%url) - end function - end module fpm_settings From ab2c5e203d152dc995e8bde000b3a8b4a47303ad Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 5 Feb 2023 17:24:25 +0100 Subject: [PATCH 079/799] Fix local tests by providing absolute path --- test/fpm_test/main.f90 | 2 +- test/fpm_test/test_settings.f90 | 57 +++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/test/fpm_test/main.f90 b/test/fpm_test/main.f90 index c8ee58df9c..3e7229bddc 100644 --- a/test/fpm_test/main.f90 +++ b/test/fpm_test/main.f90 @@ -33,7 +33,7 @@ program fpm_testing & new_testsuite("fpm_test_backend", collect_backend), & & new_testsuite("fpm_installer", collect_installer), & & new_testsuite("fpm_versioning", collect_versioning), & - ! & new_testsuite("fpm_settings", collect_settings), & + & new_testsuite("fpm_settings", collect_settings), & & new_testsuite("fpm_os", collect_os) & & ] diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 48b710b2f5..1b9f01469f 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -4,7 +4,7 @@ module test_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists use fpm_environment, only: os_is_unix use fpm_toml, only: new_table - use fpm_os, only: get_absolute_path + use fpm_os, only: get_absolute_path, get_current_directory implicit none private @@ -41,10 +41,16 @@ subroutine delete_tmp_folder if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) end - subroutine setup_global_settings(global_settings) + subroutine setup_global_settings(global_settings, error) type(fpm_global_settings), intent(out) :: global_settings + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: cwd + + call get_current_directory(cwd, error) + if (allocated(error)) return - global_settings%path_to_config_folder = tmp_folder + global_settings%path_to_config_folder = join_path(cwd, tmp_folder) global_settings%config_file_name = config_file_name end subroutine @@ -54,7 +60,8 @@ subroutine no_folder(error) type(fpm_global_settings) :: global_settings call delete_tmp_folder - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return call get_global_settings(global_settings, error) end subroutine no_folder @@ -65,7 +72,10 @@ subroutine no_file(error) call delete_tmp_folder call mkdir(tmp_folder) - call setup_global_settings(global_settings) + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) end subroutine no_file @@ -73,13 +83,16 @@ end subroutine no_file subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + character(:), allocatable :: cwd call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), ['']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) @@ -101,7 +114,9 @@ subroutine empty_registry_table(error) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) @@ -133,7 +148,9 @@ subroutine has_non_existent_path_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) end subroutine @@ -148,7 +165,9 @@ subroutine has_existent_path_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), & [character(len=10) :: '[registry]', 'path="."']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) @@ -182,7 +201,9 @@ subroutine absolute_path_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), & [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) @@ -215,7 +236,9 @@ subroutine relative_path_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -246,7 +269,9 @@ subroutine canonical_path_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), & [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -275,7 +300,9 @@ subroutine has_url_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'url="http"']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) @@ -307,7 +334,9 @@ subroutine has_both_path_and_url_to_registry(error) call filewrite(join_path(tmp_folder, config_file_name), & [character(len=10) :: '[registry]', 'path="."', 'url="http"']) - call setup_global_settings(global_settings) + call setup_global_settings(global_settings, error) + if (allocated(error)) return + call get_global_settings(global_settings, error) call os_delete_dir(os_is_unix(), tmp_folder) end subroutine From b51045f85899d0757d183d8ab2573e4e30f36a5e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 5 Feb 2023 17:28:50 +0100 Subject: [PATCH 080/799] Fix lint --- test/fpm_test/main.f90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/fpm_test/main.f90 b/test/fpm_test/main.f90 index 3e7229bddc..eb063dccab 100644 --- a/test/fpm_test/main.f90 +++ b/test/fpm_test/main.f90 @@ -85,21 +85,21 @@ subroutine get_argument(idx, arg) !> Command line argument character(len=:), allocatable, intent(out) :: arg - integer :: length, stat + integer :: length, arg_stat - call get_command_argument(idx, length=length, status=stat) - if (stat /= 0) then + call get_command_argument(idx, length=length, status=arg_stat) + if (arg_stat /= 0) then return endif - allocate(character(len=length) :: arg, stat=stat) - if (stat /= 0) then + allocate(character(len=length) :: arg, stat=arg_stat) + if (arg_stat /= 0) then return endif if (length > 0) then - call get_command_argument(idx, arg, status=stat) - if (stat /= 0) then + call get_command_argument(idx, arg, status=arg_stat) + if (arg_stat /= 0) then deallocate(arg) return end if From cceef8d14644c0b3b89e449adc0ecaa0576f9f1e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 5 Feb 2023 17:29:26 +0100 Subject: [PATCH 081/799] Remove unused variable --- test/fpm_test/test_settings.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 1b9f01469f..21a41b256a 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -83,7 +83,6 @@ end subroutine no_file subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - character(:), allocatable :: cwd call delete_tmp_folder call mkdir(tmp_folder) From 036e505ab1b835aee909813483fc80b6d5d60f23 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 6 Feb 2023 00:55:10 +0100 Subject: [PATCH 082/799] Reformat and nit syntax and comment --- src/fpm/dependency.f90 | 209 ++++++++++++++++---------------- test/fpm_test/test_settings.f90 | 16 +-- 2 files changed, 113 insertions(+), 112 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 70bf567b7f..9ccce1aedb 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -493,146 +493,147 @@ subroutine resolve_dependency(self, dependency, root, error) end subroutine resolve_dependency - !> Get a dependency from the registry. The registry can be local, custom - !> (reached via url) or the official one. - subroutine get_from_registry(dep, target_dir, error) + !> Get a dependency from the registry. Whether the dependency is fetched + !> from a local, a custom remote or the official registry is determined + !> by the global configuration settings. + subroutine get_from_registry(dep, target_dir, error) - !> Instance of the dependency configuration. - class(dependency_config_t), intent(in) :: dep + !> Instance of the dependency configuration. + class(dependency_config_t), intent(in) :: dep - !> The target directory of the dependency. - character(:), allocatable, intent(out) :: target_dir + !> The target directory of the dependency. + character(:), allocatable, intent(out) :: target_dir - !> Error handling. - type(error_t), allocatable, intent(out) :: error + !> Error handling. + type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + type(fpm_global_settings) :: global_settings - call get_global_settings(global_settings, error) - if (allocated(error)) return + call get_global_settings(global_settings, error) + if (allocated(error)) return - ! Registry settings found in the global config file. - if (allocated(global_settings%registry_settings)) then - if (allocated(global_settings%registry_settings%path)) then - ! The registry cache acts as the local registry. - call get_from_registry_cache(dep, target_dir, global_settings%registry_settings%path, error) - return - end if + ! Registry settings found in the global config file. + if (allocated(global_settings%registry_settings)) then + if (allocated(global_settings%registry_settings%path)) then + ! The registry cache acts as the local registry. + call get_from_registry_cache(dep, target_dir, global_settings%registry_settings%path, error) + return end if + end if - call get_from_registry_url(dep, target_dir, global_settings, error) + call get_from_registry_url(dep, target_dir, global_settings, error) - end subroutine get_from_registry + end subroutine get_from_registry - !> Get the dependency from the registry cache. - !> Throw error if the package isn't found. - subroutine get_from_registry_cache(dep, target_dir, cache_path, error) + !> Get the dependency from the registry cache. + !> Throw error if the package isn't found. + subroutine get_from_registry_cache(dep, target_dir, cache_path, error) - !> Instance of the dependency configuration. - class(dependency_config_t), intent(in) :: dep + !> Instance of the dependency configuration. + class(dependency_config_t), intent(in) :: dep - !> The target directory to download the dependency to. - character(:), allocatable, intent(out) :: target_dir - - !> The path to the registry cache. - character(*), intent(in) :: cache_path + !> The target directory to download the dependency to. + character(:), allocatable, intent(out) :: target_dir - !> Error handling. - type(error_t), allocatable, intent(out) :: error + !> The path to the registry cache. + character(*), intent(in) :: cache_path - character(:), allocatable :: path_to_name - type(string_t), allocatable :: files(:) - type(version_t), allocatable :: versions(:) - type(version_t) :: version - integer :: i + !> Error handling. + type(error_t), allocatable, intent(out) :: error - path_to_name = join_path(cache_path, dep%namespace, dep%name) + character(:), allocatable :: path_to_name + type(string_t), allocatable :: files(:) + type(version_t), allocatable :: versions(:) + type(version_t) :: version + integer :: i - if (.not. exists(path_to_name)) then - call fatal_error(error, "Dependency '"//dep%name//"' not found in path '"//path_to_name//"'") - return - end if + path_to_name = join_path(cache_path, dep%namespace, dep%name) - call list_files(path_to_name, files) - if (size(files) == 0) then - call fatal_error(error, "No dependencies found in '"//path_to_name//"'") - return - end if + if (.not. exists(path_to_name)) then + call fatal_error(error, "Dependency '"//dep%name//"' not found in path '"//path_to_name//"'") + return + end if - ! Version requested, find it in the cache. - if (allocated(dep%vers)) then - do i = 1, size(files) - ! Identify directory that matches the version number. - if (files(i)%s == join_path(path_to_name, dep%vers%s()) .and. is_dir(files(i)%s)) then - target_dir = files(i)%s - return - end if - end do - call fatal_error(error, "Version '"//dep%vers%s()//"' not found in '"//path_to_name//"'") - return - end if + call list_files(path_to_name, files) + if (size(files) == 0) then + call fatal_error(error, "No dependencies found in '"//path_to_name//"'") + return + end if - ! No version requested, generate list of available versions. + ! Version requested, find it in the cache. + if (allocated(dep%vers)) then do i = 1, size(files) - if (is_dir(files(i)%s)) then - call new_version(version, basename(files(i)%s), error) - if (allocated(error)) return - versions = [versions, version] + ! Identify directory that matches the version number. + if (files(i)%s == join_path(path_to_name, dep%vers%s()) .and. is_dir(files(i)%s)) then + target_dir = files(i)%s + return end if end do + call fatal_error(error, "Version '"//dep%vers%s()//"' not found in '"//path_to_name//"'") + return + end if - if (size(versions) == 0) then - call fatal_error(error, "No versions found in '"//path_to_name//"'") - return + ! No version requested, generate list of available versions. + do i = 1, size(files) + if (is_dir(files(i)%s)) then + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + versions = [versions, version] end if + end do - ! Find the latest version. - version = versions(1) - do i = 1, size(versions) - if (versions(i) > version) version = versions(i) - end do + if (size(versions) == 0) then + call fatal_error(error, "No versions found in '"//path_to_name//"'") + return + end if + + ! Find the latest version. + version = versions(1) + do i = 1, size(versions) + if (versions(i) > version) version = versions(i) + end do - target_dir = join_path(path_to_name, version%s()) - end subroutine get_from_registry_cache + target_dir = join_path(path_to_name, version%s()) + end subroutine get_from_registry_cache - !> Checks if the directory name matches the package version. - subroutine check_version(dir_path, error) + !> Checks if the directory name matches the package version. + subroutine check_version(dir_path, error) - !> Absolute path to the package-containing directory. - character(*), intent(in) :: dir_path + !> Absolute path to the package-containing directory. + character(*), intent(in) :: dir_path - !> Error handling - type(error_t), allocatable, intent(out) :: error + !> Error handling + type(error_t), allocatable, intent(out) :: error - type(toml_table), allocatable :: table - type(toml_error), allocatable :: parse_error - integer :: stat - character(:), allocatable :: version + type(toml_table), allocatable :: table + type(toml_error), allocatable :: parse_error + integer :: stat + character(:), allocatable :: version - call toml_load(table, join_path(dir_path, 'fpm.toml'), error=parse_error) + call toml_load(table, join_path(dir_path, 'fpm.toml'), error=parse_error) - if (allocated(parse_error)) then - allocate (error) - call move_alloc(parse_error%message, error%message) - return - end if + if (allocated(parse_error)) then + allocate (error) + call move_alloc(parse_error%message, error%message) + return + end if - call get_value(table, 'version', version, stat=stat) + call get_value(table, 'version', version, stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading version number from "' & - //join_path(dir_path, 'fpm.toml')//'"') - return - end if + if (stat /= toml_stat%success) then + call fatal_error(error, 'Error reading version number from "' & + //join_path(dir_path, 'fpm.toml')//'"') + return + end if - if (version /= basename(dir_path)) then - call fatal_error(error, "Directory name '"//basename(dir_path) & - //"' does not match version number '"//version//" ' in package '"// & - dir_path//"'") - return - end if + if (version /= basename(dir_path)) then + call fatal_error(error, "Directory name '"//basename(dir_path) & + //"' does not match version number '"//version//" ' in package '"// & + dir_path//"'") + return + end if - end subroutine check_version + end subroutine check_version !> Get dependency from a registry via url. subroutine get_from_registry_url(dep, target_dir, global_settings, error) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 21a41b256a..d23b55053e 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -108,7 +108,7 @@ subroutine empty_registry_table(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]']) @@ -142,7 +142,7 @@ subroutine has_non_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) @@ -158,7 +158,7 @@ subroutine has_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), & @@ -190,7 +190,7 @@ subroutine absolute_path_to_registry(error) type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call get_absolute_path(tmp_folder, abs_path, error) @@ -230,7 +230,7 @@ subroutine relative_path_to_registry(error) type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path - call delete_tmp_folder() + call delete_tmp_folder call mkdir(join_path(tmp_folder, 'abc')) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) @@ -262,7 +262,7 @@ subroutine canonical_path_to_registry(error) type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), & @@ -294,7 +294,7 @@ subroutine has_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'url="http"']) @@ -327,7 +327,7 @@ subroutine has_both_path_and_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - call delete_tmp_folder() + call delete_tmp_folder call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), & From fcca8e2f2d34a2ff3129ac907196d03abd3ee392 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 7 Feb 2023 01:46:53 +0100 Subject: [PATCH 083/799] Fix version comparison --- src/fpm/versioning.f90 | 9 +++--- test/fpm_test/test_versioning.f90 | 46 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/fpm/versioning.f90 b/src/fpm/versioning.f90 index 4007fdf55d..4c7c01712a 100644 --- a/src/fpm/versioning.f90 +++ b/src/fpm/versioning.f90 @@ -299,16 +299,17 @@ elemental function greater(lhs, rhs) result(is_greater) integer :: ii do ii = 1, min(size(lhs%num), size(rhs%num)) - is_greater = lhs%num(ii) > rhs%num(ii) - if (is_greater) exit + if (lhs%num(ii) /= rhs%num(ii)) then + is_greater = lhs%num(ii) > rhs%num(ii) + return + end if end do - if (is_greater) return is_greater = size(lhs%num) > size(rhs%num) if (is_greater) then do ii = size(rhs%num) + 1, size(lhs%num) is_greater = lhs%num(ii) > 0 - if (is_greater) exit + if (is_greater) return end do end if diff --git a/test/fpm_test/test_versioning.f90 b/test/fpm_test/test_versioning.f90 index c24149185a..fcaffbb015 100644 --- a/test/fpm_test/test_versioning.f90 +++ b/test/fpm_test/test_versioning.f90 @@ -262,6 +262,52 @@ subroutine test_valid_compare(error) return end if + call new_version(v1, [1, 2, 3]) + call new_version(v2, [2, 0, 0]) + + if (v1 > v2) then + call test_failed(error, "Version comparison failed (gt)") + return + end if + + if (v1 >= v2) then + call test_failed(error, "Version comparison failed (ge)") + return + end if + + if (v2 < v1) then + call test_failed(error, "Version comparison failed (lt)") + return + end if + + if (v2 <= v1) then + call test_failed(error, "Version comparison failed (le)") + return + end if + + call new_version(v1, [1, 2, 3]) + call new_version(v2, [1, 0, 4]) + + if (v2 > v1) then + call test_failed(error, "Version comparison failed (gt)") + return + end if + + if (v2 >= v1) then + call test_failed(error, "Version comparison failed (ge)") + return + end if + + if (v1 < v2) then + call test_failed(error, "Version comparison failed (lt)") + return + end if + + if (v1 <= v2) then + call test_failed(error, "Version comparison failed (le)") + return + end if + end subroutine test_valid_compare From 20151d44b3b443f86a4d49a20acd95b9b382de3b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 7 Feb 2023 02:14:52 +0100 Subject: [PATCH 084/799] Reformat and improve comments --- src/fpm/dependency.f90 | 3 +- src/fpm/manifest/dependency.f90 | 44 +++++++++------------ src/fpm/toml.f90 | 21 +++++----- src/fpm_os.F90 | 4 +- src/fpm_settings.f90 | 19 ++++----- test/fpm_test/test_manifest.f90 | 2 +- test/fpm_test/test_package_dependencies.f90 | 13 +++--- 7 files changed, 49 insertions(+), 57 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 9ccce1aedb..4236fce5ee 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -507,7 +507,8 @@ subroutine get_from_registry(dep, target_dir, error) !> Error handling. type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + !> Global configuration settings. + type(fpm_global_settings), optional, intent(inout) :: global_settings call get_global_settings(global_settings, error) if (allocated(error)) return diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index a57030b6f5..9a1e4c7d87 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -35,7 +35,6 @@ module fpm_manifest_dependency public :: dependency_config_t, new_dependency, new_dependencies - !> Configuration meta data for a dependency type :: dependency_config_t @@ -64,10 +63,8 @@ module fpm_manifest_dependency end type dependency_config_t - contains - !> Construct a new dependency configuration from a TOML data structure subroutine new_dependency(self, table, root, error) @@ -106,21 +103,21 @@ subroutine new_dependency(self, table, root, error) self%git = git_target_tag(uri, value) end if - if (.not.allocated(self%git)) then + if (.not. allocated(self%git)) then call get_value(table, "branch", value) if (allocated(value)) then self%git = git_target_branch(uri, value) end if end if - if (.not.allocated(self%git)) then + if (.not. allocated(self%git)) then call get_value(table, "rev", value) if (allocated(value)) then self%git = git_target_revision(uri, value) end if end if - if (.not.allocated(self%git)) then + if (.not. allocated(self%git)) then self%git = git_target_default(uri) end if return @@ -135,7 +132,6 @@ subroutine new_dependency(self, table, root, error) end subroutine new_dependency - !> Check local schema for allowed entries subroutine check(table, error) @@ -192,24 +188,24 @@ subroutine check(table, error) call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") return end if - - if ((table%has_key("branch") .and. table%has_key("rev")) .or.& - (table%has_key("branch") .and. table%has_key("tag")) .or.& - (table%has_key("rev") .and. table%has_key("tag"))) then + + if ((table%has_key("branch") .and. table%has_key("rev")) .or. & + (table%has_key("branch") .and. table%has_key("tag")) .or. & + (table%has_key("rev") .and. table%has_key("tag"))) then call syntax_error(error, "Dependency '"//name//"' can only have one of branch, rev or tag present") return end if - - if ((table%has_key("branch") .or. table%has_key("tag") .or. table%has_key("rev"))& + + if ((table%has_key("branch") .or. table%has_key("tag") .or. table%has_key("rev")) & .and. .not. table%has_key("git")) then call syntax_error(error, "Dependency '"//name//"' has git identifier but no git url") return end if - - if (.not. table%has_key("path") .and. .not. table%has_key("git")& + + if (.not. table%has_key("path") .and. .not. table%has_key("git") & .and. .not. table%has_key("namespace")) then - call syntax_error(error, "Please provide a 'namespace' for dependency '"//name//& - "' if it is not a local path or git repository") + call syntax_error(error, "Please provide a 'namespace' for dependency '"//name// & + & "' if it is not a local path or git repository") return end if @@ -243,7 +239,7 @@ subroutine new_dependencies(deps, table, root, error) ! An empty table is okay if (size(list) < 1) return - allocate(deps(size(list))) + allocate (deps(size(list))) do idep = 1, size(list) call get_value(table, list(idep)%key, node, stat=stat) if (stat /= toml_stat%success) then @@ -256,7 +252,6 @@ subroutine new_dependencies(deps, table, root, error) end subroutine new_dependencies - !> Write information on instance subroutine info(self, unit, verbosity) @@ -278,22 +273,21 @@ subroutine info(self, unit, verbosity) pr = 1 end if - write(unit, fmt) "Dependency" + write (unit, fmt) "Dependency" if (allocated(self%name)) then - write(unit, fmt) "- name", self%name + write (unit, fmt) "- name", self%name end if if (allocated(self%git)) then - write(unit, fmt) "- kind", "git" + write (unit, fmt) "- kind", "git" call self%git%info(unit, pr - 1) end if if (allocated(self%path)) then - write(unit, fmt) "- kind", "local" - write(unit, fmt) "- path", self%path + write (unit, fmt) "- kind", "local" + write (unit, fmt) "- path", self%path end if end subroutine info - end module fpm_manifest_dependency diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 9d71474b6b..eea5a5db91 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -13,9 +13,9 @@ !> For more details on the library used see the !> [TOML-Fortran](https://toml-f.github.io/toml-f) developer pages. module fpm_toml - use fpm_error, only : error_t, fatal_error, file_not_found_error - use fpm_strings, only : string_t - use tomlf, only : toml_table, toml_array, toml_key, toml_stat, get_value, & + use fpm_error, only: error_t, fatal_error, file_not_found_error + use fpm_strings, only: string_t + use tomlf, only: toml_table, toml_array, toml_key, toml_stat, get_value, & & set_value, toml_parse, toml_error, new_table, add_table, add_array, & & toml_serializer, len, toml_load implicit none @@ -45,26 +45,25 @@ subroutine read_package_file(table, manifest, error) integer :: unit logical :: exist - inquire(file=manifest, exist=exist) + inquire (file=manifest, exist=exist) - if (.not.exist) then + if (.not. exist) then call file_not_found_error(error, manifest) return end if - open(file=manifest, newunit=unit) + open (file=manifest, newunit=unit) call toml_parse(table, unit, parse_error) - close(unit) + close (unit) if (allocated(parse_error)) then - allocate(error) + allocate (error) call move_alloc(parse_error%message, error%message) return end if end subroutine read_package_file - subroutine get_list(table, key, list, error) !> Instance of the TOML data structure @@ -86,7 +85,7 @@ subroutine get_list(table, key, list, error) call get_value(table, key, children, requested=.false.) if (associated(children)) then nlist = len(children) - allocate(list(nlist)) + allocate (list(nlist)) do ilist = 1, nlist call get_value(children, ilist, str, stat=stat) if (stat /= toml_stat%success) then @@ -103,7 +102,7 @@ subroutine get_list(table, key, list, error) return end if if (allocated(str)) then - allocate(list(1)) + allocate (list(1)) call move_alloc(str, list(1)%s) end if end if diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index a59586d0ad..ec61cd732a 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -136,7 +136,7 @@ subroutine get_realpath(path, real_path, error) type(c_ptr) :: ptr if (.not. exists(path)) then - call fatal_error(error, "Cannot get real path. Path '"//path//"' does not exist") + call fatal_error(error, "Cannot determine absolute path. Path '"//path//"' does not exist.") return end if @@ -155,7 +155,7 @@ subroutine get_realpath(path, real_path, error) if (c_associated(ptr)) then call c_f_character(cpath, real_path) else - call fatal_error(error, "Failed to retrieve real path for '"//path//"'") + call fatal_error(error, "Failed to retrieve absolute path for '"//path//"'.") end if end subroutine get_realpath diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 2234a081af..6aa59b5950 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -52,13 +52,13 @@ subroutine get_global_settings(global_settings, error) if (global_settings%has_custom_location()) then ! Throw error if folder doesn't exist. if (.not. exists(global_settings%path_to_config_folder)) then - call fatal_error(error, 'Folder not found: "'//global_settings%path_to_config_folder//'"') + call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'.") return end if ! Throw error if file doesn't exist. if (.not. exists(global_settings%full_path())) then - call fatal_error(error, 'File not found: "'//global_settings%full_path()//'"') + call fatal_error(error, "File not found: '"//global_settings%full_path()//"'.") return end if @@ -100,6 +100,7 @@ subroutine get_registry_settings(global_settings, table, error) type(fpm_global_settings), intent(inout) :: global_settings type(toml_table), intent(inout) :: table type(error_t), allocatable, intent(out) :: error + type(toml_table), pointer :: child character(:), allocatable :: path, url, cache_path integer :: stat @@ -120,7 +121,7 @@ subroutine get_registry_settings(global_settings, table, error) call get_value(child, 'path', path, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading registry path: "'//path//'"') + call fatal_error(error, "Error reading registry path: '"//path//"'.") return end if @@ -130,13 +131,13 @@ subroutine get_registry_settings(global_settings, table, error) else ! Get canonical, absolute path on both Unix and Windows. call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & - global_settings%registry_settings%path, error) + & global_settings%registry_settings%path, error) if (allocated(error)) return ! Check if the path to the registry exists. if (.not. exists(global_settings%registry_settings%path)) then call fatal_error(error, "Directory '"//global_settings%registry_settings%path// & - "' does not exist") + & "' doesn't exist.") return end if end if @@ -145,14 +146,14 @@ subroutine get_registry_settings(global_settings, table, error) call get_value(child, 'url', url, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading registry url: "'//url//'"') + call fatal_error(error, "Error reading registry url: '"//url//"'.") return end if if (allocated(url)) then ! Throw error when both path and url were provided. if (allocated(path)) then - call fatal_error(error, 'Do not provide both path and url to the registry') + call fatal_error(error, 'Do not provide both path and url to the registry.') return end if @@ -162,14 +163,14 @@ subroutine get_registry_settings(global_settings, table, error) call get_value(child, 'cache_path', cache_path, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading path to registry cache: "'//cache_path//'"') + call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'.") return end if if (allocated(cache_path)) then ! Throw error when both path and cache_path were provided. if (allocated(path)) then - call fatal_error(error, "Do not provide both 'path' and 'cache_path'") + call fatal_error(error, "Do not provide both 'path' and 'cache_path'.") return end if diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index b870dc38be..0f0c633f8e 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -349,7 +349,7 @@ subroutine test_dependency_gitconflict(error) end subroutine test_dependency_gitconflict - !> Try to create a git dependency with invalid source format + !> Try to create a git dependency with an invalid source format. subroutine test_dependency_invalid_git(error) use fpm_manifest_dependency use fpm_toml, only : new_table, toml_table, set_value diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 26f6852a0e..bbc2abfd07 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -59,15 +59,15 @@ subroutine test_cache_dump_load(error) dep%path = "fpm-tmp3-dir" call new_dependency_node(deps%dep(3), dep, proj_dir=dep%path) - open(newunit=unit, status='scratch') + open (newunit=unit, status='scratch') call deps%dump(unit, error) - if (.not.allocated(error)) then - rewind(unit) + if (.not. allocated(error)) then + rewind (unit) call new_dependency_tree(deps) call resize(deps%dep, 2) call deps%load(unit, error) - close(unit) + close (unit) end if if (allocated(error)) return @@ -78,7 +78,6 @@ subroutine test_cache_dump_load(error) end subroutine test_cache_dump_load - !> Round trip of the dependency cache from a TOML data structure to !> a dependency tree to a TOML data structure subroutine test_cache_load_dump(error) @@ -131,7 +130,6 @@ subroutine test_cache_load_dump(error) end subroutine test_cache_load_dump - subroutine test_status(error) !> Error handling @@ -161,7 +159,6 @@ subroutine test_status(error) end subroutine test_status - subroutine test_add_dependencies(error) !> Error handling @@ -206,7 +203,7 @@ subroutine test_add_dependencies(error) call deps%resolve(".", error) if (allocated(error)) return - if (.not.deps%finished()) then + if (.not. deps%finished()) then call test_failed(error, "Mocked dependency tree must resolve in one step") return end if From 4176a903954b74e3cd7091f87de255852eef9c7d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 7 Feb 2023 02:19:23 +0100 Subject: [PATCH 085/799] Refactor checking of table keys and reuse --- src/fpm/manifest/dependency.f90 | 53 +++++++++++---------------------- src/fpm/toml.f90 | 48 +++++++++++++++++++++++++++-- src/fpm_settings.f90 | 16 ++++++++-- test/fpm_test/test_settings.f90 | 34 +++++++++++++++++++++ 4 files changed, 110 insertions(+), 41 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 9a1e4c7d87..04a6b4d78a 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -23,13 +23,13 @@ !> Resolving a dependency will result in obtaining a new package configuration !> data for the respective project. module fpm_manifest_dependency - use fpm_error, only : error_t, syntax_error - use fpm_git, only : git_target_t, git_target_tag, git_target_branch, & + use fpm_error, only: error_t, syntax_error + use fpm_git, only: git_target_t, git_target_tag, git_target_branch, & & git_target_revision, git_target_default - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS - use fpm_versioning, only : version_t, new_version + use fpm_versioning, only: version_t implicit none private @@ -123,9 +123,10 @@ subroutine new_dependency(self, table, root, error) return end if - call get_value(table, 'vers', version) + call get_value(table, "vers", version) if (allocated(version)) then + if (.not. allocated(self%vers)) allocate (self%vers) call new_version(self%vers, version, error) if (allocated(error)) return end if @@ -141,18 +142,17 @@ subroutine check(table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: name, value, valid_keys_string + character(len=:), allocatable :: name type(toml_key), allocatable :: list(:) - integer :: ikey, ivalid - - !> List of allowed keys for the dependency table - character(*), dimension(*), parameter :: valid_keys = [character(24) ::& - & "namespace",& - "vers",& - "path",& - "git",& - "tag",& - "branch",& + + !> List of valid keys for the dependency table. + character(*), dimension(*), parameter :: valid_keys = [character(24) :: & + & "namespace", & + "vers", & + "path", & + "git", & + "tag", & + "branch", & "rev" & & ] @@ -164,25 +164,8 @@ subroutine check(table, error) return end if - do ikey = 1, size(list) - if (.not. any(list(ikey)%key == valid_keys)) then - ! Improve error message - valid_keys_string = new_line('a')//new_line('a') - do ivalid = 1, size(valid_keys) - valid_keys_string = valid_keys_string//trim(valid_keys(ivalid))//new_line('a') - end do - call syntax_error(error, "Key '"//list(ikey)%key//"' not allowed in dependency '"//& - name//"'."//new_line('a')//new_line('a')//'Valid keys: '//valid_keys_string) - return - end if - - ! Check if value can be mapped or else (wrong type) show error message with the error location - call get_value(table, list(ikey)%key, value) - if (.not. allocated(value)) then - call syntax_error(error, "Dependency '"//name//"' has invalid '"//list(ikey)%key//"' entry") - return - end if - end do + call check_keys(table, valid_keys, error) + if (allocated(error)) return if (table%has_key("path") .and. table%has_key("git")) then call syntax_error(error, "Dependency '"//name//"' cannot have both git and path entries") diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index eea5a5db91..5d87b20673 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -23,12 +23,10 @@ module fpm_toml public :: read_package_file, toml_table, toml_array, toml_key, toml_stat, & get_value, set_value, get_list, new_table, add_table, add_array, len, & - toml_error, toml_serializer, toml_parse, toml_load - + toml_error, toml_serializer, toml_parse, toml_load, check_keys contains - !> Process the configuration file to a TOML data structure subroutine read_package_file(table, manifest, error) @@ -109,5 +107,49 @@ subroutine get_list(table, key, list, error) end subroutine get_list + !> Check if table contains only keys that are part of the list. If a key is + !> found that is not part of the list, an error is allocated. + subroutine check_keys(table, valid_keys, error) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> List of keys to check. + character(len=*), intent(in) :: valid_keys(:) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_key), allocatable :: keys(:) + character(:), allocatable :: name, value, valid_keys_string + integer :: ikey, ivalid + + call table%get_key(name) + call table%get_keys(keys) + + do ikey = 1, size(keys) + if (.not. any(keys(ikey)%key == valid_keys)) then + ! Generate error message + valid_keys_string = new_line('a')//new_line('a') + do ivalid = 1, size(valid_keys) + valid_keys_string = valid_keys_string//trim(valid_keys(ivalid))//new_line('a') + end do + allocate (error) + error%message = "Key '"//keys(ikey)%key//"' not allowed in the '"// & + & name//"' table."//new_line('a')//new_line('a')//'Valid keys: '//valid_keys_string + return + end if + + ! Check if value can be mapped or else (wrong type) show error message with the error location. + ! Right now, it can only be mapped to a string, but this can be extended in the future. + call get_value(table, keys(ikey)%key, value) + if (.not. allocated(value)) then + allocate (error) + error%message = "'"//name//"' has an invalid '"//keys(ikey)%key//"' entry." + return + end if + end do + + end subroutine check_keys end module fpm_toml diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 6aa59b5950..28071be3ec 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -3,7 +3,7 @@ module fpm_settings use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path, mkdir use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error - use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load + use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load, check_keys use fpm_os, only: get_current_directory, change_directory, get_absolute_path, & convert_to_absolute_path implicit none @@ -105,17 +105,27 @@ subroutine get_registry_settings(global_settings, table, error) character(:), allocatable :: path, url, cache_path integer :: stat + !> List of valid keys for the dependency table. + character(*), dimension(*), parameter :: valid_keys = [character(10) :: & + & 'path', & + & 'url', & + & 'cache_path' & + & ] + call get_value(table, 'registry', child, requested=.false., stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading registry from config file "'// & - global_settings%full_path()//'"') + call fatal_error(error, "Error reading registry from config file '"// & + & global_settings%full_path()//"'.") return end if ! Quietly return if no registry table was found. if (.not. associated(child)) return + call check_keys(child, valid_keys, error) + if (allocated(error)) return + allocate (global_settings%registry_settings) call get_value(child, 'path', path, stat=stat) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index d23b55053e..ffba27a5ca 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -26,6 +26,8 @@ subroutine collect_settings(tests) & new_unittest('no-file', no_file, should_fail=.true.), & & new_unittest('empty-file', empty_file), & & new_unittest('empty-registry-table', empty_registry_table), & + & new_unittest('wrong-key', wrong_key, should_fail=.true.), & + & new_unittest('wrong-type', wrong_type, should_fail=.true.), & & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & @@ -138,6 +140,38 @@ subroutine empty_registry_table(error) end if end subroutine + subroutine wrong_key(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + + call delete_tmp_folder + call mkdir(tmp_folder) + + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'abcd="abc"']) ! Invalid key + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + subroutine wrong_type(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + + call delete_tmp_folder + call mkdir(tmp_folder) + + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path=12345']) ! Value not a string + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + subroutine has_non_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings From 26fb9ed3bd93683437613fc9c73068a558fe191e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 7 Feb 2023 02:23:45 +0100 Subject: [PATCH 086/799] Refactor to member procedures, improve names, fix bugs, add tests for the local registry --- src/fpm/dependency.f90 | 45 +-- test/fpm_test/test_package_dependencies.f90 | 298 +++++++++++++++++++- 2 files changed, 312 insertions(+), 31 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 4236fce5ee..2312c72ecb 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -95,7 +95,7 @@ module fpm_dependency logical :: update = .false. contains !> Update dependency from project manifest - procedure :: register + procedure :: register, get_from_registry, get_from_local_registry, get_from_remote_registry end type dependency_node_t @@ -466,7 +466,7 @@ subroutine resolve_dependency(self, dependency, root, error) if (allocated(error)) return end if else - call get_from_registry(dependency, proj_dir, error) + call dependency%get_from_registry(proj_dir, error) if (allocated(error)) return end if @@ -496,10 +496,10 @@ end subroutine resolve_dependency !> Get a dependency from the registry. Whether the dependency is fetched !> from a local, a custom remote or the official registry is determined !> by the global configuration settings. - subroutine get_from_registry(dep, target_dir, error) + subroutine get_from_registry(self, target_dir, error, global_settings) !> Instance of the dependency configuration. - class(dependency_config_t), intent(in) :: dep + class(dependency_node_t), intent(in) :: self !> The target directory of the dependency. character(:), allocatable, intent(out) :: target_dir @@ -517,21 +517,20 @@ subroutine get_from_registry(dep, target_dir, error) if (allocated(global_settings%registry_settings)) then if (allocated(global_settings%registry_settings%path)) then ! The registry cache acts as the local registry. - call get_from_registry_cache(dep, target_dir, global_settings%registry_settings%path, error) - return + call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error) + return end if end if - call get_from_registry_url(dep, target_dir, global_settings, error) + call self%get_from_remote_registry(target_dir, global_settings, error) end subroutine get_from_registry - !> Get the dependency from the registry cache. - !> Throw error if the package isn't found. - subroutine get_from_registry_cache(dep, target_dir, cache_path, error) + !> Get the dependency from a local registry. + subroutine get_from_local_registry(self, target_dir, cache_path, error) !> Instance of the dependency configuration. - class(dependency_config_t), intent(in) :: dep + class(dependency_node_t), intent(in) :: self !> The target directory to download the dependency to. character(:), allocatable, intent(out) :: target_dir @@ -548,33 +547,35 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) type(version_t) :: version integer :: i - path_to_name = join_path(cache_path, dep%namespace, dep%name) + path_to_name = join_path(cache_path, self%namespace, self%name) if (.not. exists(path_to_name)) then - call fatal_error(error, "Dependency '"//dep%name//"' not found in path '"//path_to_name//"'") + call fatal_error(error, "Dependency resolution of '"//self%name// & + & "': Directory '"//path_to_name//"' doesn't exist.") return end if call list_files(path_to_name, files) if (size(files) == 0) then - call fatal_error(error, "No dependencies found in '"//path_to_name//"'") + call fatal_error(error, "No versions of '"//self%name//"' found in '"//path_to_name//"'.") return end if ! Version requested, find it in the cache. - if (allocated(dep%vers)) then + if (allocated(self%vers)) then do i = 1, size(files) ! Identify directory that matches the version number. - if (files(i)%s == join_path(path_to_name, dep%vers%s()) .and. is_dir(files(i)%s)) then + if (files(i)%s == join_path(path_to_name, self%vers%s()) .and. is_dir(files(i)%s)) then target_dir = files(i)%s return end if end do - call fatal_error(error, "Version '"//dep%vers%s()//"' not found in '"//path_to_name//"'") + call fatal_error(error, "Version '"//self%vers%s()//"' not found in '"//path_to_name//"'") return end if ! No version requested, generate list of available versions. + allocate (versions(0)) do i = 1, size(files) if (is_dir(files(i)%s)) then call new_version(version, basename(files(i)%s), error) @@ -595,7 +596,7 @@ subroutine get_from_registry_cache(dep, target_dir, cache_path, error) end do target_dir = join_path(path_to_name, version%s()) - end subroutine get_from_registry_cache + end subroutine get_from_local_registry !> Checks if the directory name matches the package version. subroutine check_version(dir_path, error) @@ -636,11 +637,11 @@ subroutine check_version(dir_path, error) end subroutine check_version - !> Get dependency from a registry via url. - subroutine get_from_registry_url(dep, target_dir, global_settings, error) + !> Get the dependency from a remote registry. + subroutine get_from_remote_registry(self, target_dir, global_settings, error) !> Instance of the dependency configuration. - class(dependency_config_t), intent(in) :: dep + class(dependency_node_t), intent(in) :: self !> The target directory to download the dependency to. character(:), allocatable, intent(out) :: target_dir @@ -670,7 +671,7 @@ subroutine get_from_registry_url(dep, target_dir, global_settings, error) end if ! Get new versions from the registry, sending existing versions. ! Put them in the cache. - end subroutine get_from_registry_url + end subroutine get_from_remote_registry !> True if dependency is part of the tree pure logical function has_dependency(self, dependency) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index bbc2abfd07..12a6d0869c 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -1,40 +1,51 @@ !> Define tests for the `fpm_dependency` module module test_package_dependencies use fpm_filesystem, only: get_temp_filename - use testsuite, only : new_unittest, unittest_t, error_t, test_failed + use testsuite, only: new_unittest, unittest_t, error_t, test_failed + use fpm_filesystem, only: is_dir, join_path, filewrite, mkdir, os_delete_dir + use fpm_environment, only: os_is_unix + use fpm_os, only: get_current_directory use fpm_dependency - use fpm_manifest use fpm_manifest_dependency use fpm_toml + use fpm_settings, only: fpm_global_settings + implicit none private public :: collect_package_dependencies + character(*), parameter :: tmp_folder = 'tmp' + character(*), parameter :: config_file_name = 'config.toml' + type, extends(dependency_tree_t) :: mock_dependency_tree_t contains procedure :: resolve_dependency => resolve_dependency_once end type mock_dependency_tree_t - contains - !> Collect all exported unit tests - subroutine collect_package_dependencies(testsuite) + subroutine collect_package_dependencies(tests) !> Collection of tests - type(unittest_t), allocatable, intent(out) :: testsuite(:) + type(unittest_t), allocatable, intent(out) :: tests(:) - testsuite = [ & + tests = [ & & new_unittest("cache-load-dump", test_cache_load_dump), & & new_unittest("cache-dump-load", test_cache_dump_load), & & new_unittest("status-after-load", test_status), & - & new_unittest("add-dependencies", test_add_dependencies)] + & new_unittest("add-dependencies", test_add_dependencies), & + & new_unittest("registry-dir-not-found", test_registry_dir_not_found, should_fail=.true.), & + & new_unittest("no-versions-in-registry", test_no_versions_in_registry, should_fail=.true.), & + & new_unittest("version-not-found-in-registry", test_version_not_found_in_registry, should_fail=.true.), & + & new_unittest("version-found-in-registry", test_version_found_in_registry), & + & new_unittest("test-no-dir-in-registry", test_no_dir_in_registry, should_fail=.true.), & + & new_unittest("test-newest-version-in-registry", test_newest_version_in_registry) & + & ] end subroutine collect_package_dependencies - !> Round trip of the dependency cache from a dependency tree to a TOML document !> to a dependency tree subroutine test_cache_dump_load(error) @@ -210,6 +221,259 @@ subroutine test_add_dependencies(error) end subroutine test_add_dependencies + subroutine test_registry_dir_not_found(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache')) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=12) :: '[registry]', 'path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, error, global_settings) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine test_registry_dir_not_found + + subroutine test_no_versions_in_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=12) :: '[registry]', 'path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, error, global_settings) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine test_no_versions_in_registry + + subroutine test_version_not_found_in_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'vers', '0.1.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.9')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.1')) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=12) :: '[registry]', 'path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, error, global_settings) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine test_version_not_found_in_registry + + subroutine test_version_found_in_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'vers', '0.1.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.2.0')) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=12) :: '[registry]', 'path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, error, global_settings) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'))) then + call test_failed(error, 'target_dir not set correctly') + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine test_version_found_in_registry + + subroutine test_no_dir_in_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) + call filewrite(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', 'abc'), ['abc']) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=12) :: '[registry]', 'path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, error, global_settings) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine test_no_dir_in_registry + + subroutine test_newest_version_in_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=12) :: '[registry]', 'path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, error, global_settings) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'))) then + call test_failed(error, 'target_dir not set correctly') + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine test_newest_version_in_registry !> Resolve a single dependency node subroutine resolve_dependency_once(self, dependency, root, error) @@ -230,5 +494,21 @@ subroutine resolve_dependency_once(self, dependency, root, error) end subroutine resolve_dependency_once + subroutine delete_tmp_folder + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + end + + subroutine setup_global_settings(global_settings, error) + type(fpm_global_settings), intent(out) :: global_settings + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: cwd + + call get_current_directory(cwd, error) + if (allocated(error)) return + + global_settings%path_to_config_folder = join_path(cwd, tmp_folder) + global_settings%config_file_name = config_file_name + end subroutine end module test_package_dependencies From 3a3df068c13dc409b6e7e9b3aa1ff7622cb46b4d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 7 Feb 2023 02:30:50 +0100 Subject: [PATCH 087/799] Add missing import --- src/fpm/manifest/dependency.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 04a6b4d78a..d6e9017c89 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -29,7 +29,7 @@ module fpm_manifest_dependency use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS - use fpm_versioning, only: version_t + use fpm_versioning, only: version_t, new_version implicit none private From 79cefa567c95d81219bf360ad8bc3a5869ce3337 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 7 Feb 2023 16:20:52 +0100 Subject: [PATCH 088/799] Add more tests --- test/fpm_test/test_versioning.f90 | 52 +++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/test/fpm_test/test_versioning.f90 b/test/fpm_test/test_versioning.f90 index fcaffbb015..f678c9730e 100644 --- a/test/fpm_test/test_versioning.f90 +++ b/test/fpm_test/test_versioning.f90 @@ -12,12 +12,12 @@ module test_versioning !> Collect all exported unit tests - subroutine collect_versioning(testsuite) + subroutine collect_versioning(tests) !> Collection of tests - type(unittest_t), allocatable, intent(out) :: testsuite(:) + type(unittest_t), allocatable, intent(out) :: tests(:) - testsuite = [ & + tests = [ & & new_unittest("valid-version", test_valid_version), & & new_unittest("valid-equals", test_valid_equals), & & new_unittest("valid-notequals", test_valid_notequals), & @@ -308,6 +308,52 @@ subroutine test_valid_compare(error) return end if + call new_version(v1, [1, 0, 8]) + call new_version(v2, [1]) + + if (.not. v1 > v2) then + call test_failed(error, "Version comparison failed (gt)") + return + end if + + if (.not. v1 >= v2) then + call test_failed(error, "Version comparison failed (ge)") + return + end if + + if (.not. v2 < v1) then + call test_failed(error, "Version comparison failed (lt)") + return + end if + + if (.not. v2 <= v1) then + call test_failed(error, "Version comparison failed (le)") + return + end if + + call new_version(v1, [1]) + call new_version(v2, [1, 0, 8]) + + if (v1 > v2) then + call test_failed(error, "Version comparison failed (gt)") + return + end if + + if (v1 >= v2) then + call test_failed(error, "Version comparison failed (ge)") + return + end if + + if (v2 < v1) then + call test_failed(error, "Version comparison failed (lt)") + return + end if + + if (v2 <= v1) then + call test_failed(error, "Version comparison failed (le)") + return + end if + end subroutine test_valid_compare From fa2d9fc0e06197b35cb46481e18892c865a239de Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Feb 2023 16:21:16 +0100 Subject: [PATCH 089/799] Remove code duplication --- src/fpm/dependency.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 2312c72ecb..3e59f515c3 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -850,10 +850,6 @@ subroutine load_from_toml(self, table, error) call new_version(dep%version, version, error) if (allocated(error)) exit end if - if (allocated(version)) then - call new_version(dep%version, version, error) - if (allocated(error)) exit - end if if (allocated(url)) then if (allocated(obj)) then dep%git = git_target_revision(url, obj) From 49e1a7f4ef1c570c172e8b7b816afe70600e755c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Feb 2023 23:16:05 +0100 Subject: [PATCH 090/799] Improve naming --- src/fpm/dependency.f90 | 15 +++++++-------- src/fpm/manifest/dependency.f90 | 18 +++++++++--------- src/fpm_settings.f90 | 2 +- test/fpm_test/test_manifest.f90 | 4 ++-- test/fpm_test/test_package_dependencies.f90 | 4 ++-- test/fpm_test/test_settings.f90 | 6 +++--- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 3e59f515c3..5bb5a20ca2 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -516,7 +516,6 @@ subroutine get_from_registry(self, target_dir, error, global_settings) ! Registry settings found in the global config file. if (allocated(global_settings%registry_settings)) then if (allocated(global_settings%registry_settings%path)) then - ! The registry cache acts as the local registry. call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error) return end if @@ -527,7 +526,7 @@ subroutine get_from_registry(self, target_dir, error, global_settings) end subroutine get_from_registry !> Get the dependency from a local registry. - subroutine get_from_local_registry(self, target_dir, cache_path, error) + subroutine get_from_local_registry(self, target_dir, registry_path, error) !> Instance of the dependency configuration. class(dependency_node_t), intent(in) :: self @@ -535,8 +534,8 @@ subroutine get_from_local_registry(self, target_dir, cache_path, error) !> The target directory to download the dependency to. character(:), allocatable, intent(out) :: target_dir - !> The path to the registry cache. - character(*), intent(in) :: cache_path + !> The path to the local registry. + character(*), intent(in) :: registry_path !> Error handling. type(error_t), allocatable, intent(out) :: error @@ -547,7 +546,7 @@ subroutine get_from_local_registry(self, target_dir, cache_path, error) type(version_t) :: version integer :: i - path_to_name = join_path(cache_path, self%namespace, self%name) + path_to_name = join_path(registry_path, self%namespace, self%name) if (.not. exists(path_to_name)) then call fatal_error(error, "Dependency resolution of '"//self%name// & @@ -562,15 +561,15 @@ subroutine get_from_local_registry(self, target_dir, cache_path, error) end if ! Version requested, find it in the cache. - if (allocated(self%vers)) then + if (allocated(self%requested_version)) then do i = 1, size(files) ! Identify directory that matches the version number. - if (files(i)%s == join_path(path_to_name, self%vers%s()) .and. is_dir(files(i)%s)) then + if (files(i)%s == join_path(path_to_name, self%requested_version%s()) .and. is_dir(files(i)%s)) then target_dir = files(i)%s return end if end do - call fatal_error(error, "Version '"//self%vers%s()//"' not found in '"//path_to_name//"'") + call fatal_error(error, "Version '"//self%requested_version%s()//"' not found in '"//path_to_name//"'") return end if diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index d6e9017c89..3114df7bb2 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -49,9 +49,9 @@ module fpm_manifest_dependency !> Required for dependencies that are obtained via the official registry. character(len=:), allocatable :: namespace - !> The specified version of the dependency. + !> The requested version of the dependency. !> The latest version is used if not specified. - type(version_t), allocatable :: vers + type(version_t), allocatable :: requested_version !> Git descriptor type(git_target_t), allocatable :: git @@ -80,7 +80,7 @@ subroutine new_dependency(self, table, root, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: uri, value, version + character(len=:), allocatable :: uri, value, requested_version call check(table, error) if (allocated(error)) return @@ -123,11 +123,11 @@ subroutine new_dependency(self, table, root, error) return end if - call get_value(table, "vers", version) + call get_value(table, "v", requested_version) - if (allocated(version)) then - if (.not. allocated(self%vers)) allocate (self%vers) - call new_version(self%vers, version, error) + if (allocated(requested_version)) then + if (.not. allocated(self%requested_version)) allocate (self%requested_version) + call new_version(self%requested_version, requested_version, error) if (allocated(error)) return end if @@ -148,7 +148,7 @@ subroutine check(table, error) !> List of valid keys for the dependency table. character(*), dimension(*), parameter :: valid_keys = [character(24) :: & & "namespace", & - "vers", & + "v", & "path", & "git", & "tag", & @@ -192,7 +192,7 @@ subroutine check(table, error) return end if - if (table%has_key('vers') .and. (table%has_key('path') .or. table%has_key('git'))) then + if (table%has_key('v') .and. (table%has_key('path') .or. table%has_key('git'))) then call syntax_error(error, "Dependency '"//name//"' cannot have both vers and git/path entries") return end if diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 28071be3ec..002b6e332a 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -193,7 +193,7 @@ subroutine get_registry_settings(global_settings, table, error) end if ! Get canonical, absolute path on both Unix and Windows. call get_absolute_path(join_path(global_settings%path_to_config_folder, cache_path), & - global_settings%registry_settings%cache_path, error) + & global_settings%registry_settings%cache_path, error) if (allocated(error)) return end if ! Both path and cache_path not allocated, use default location for cache_path. diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 0f0c633f8e..82ee61a4ae 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -380,7 +380,7 @@ subroutine test_dependency_no_namespace(error) call new_table(table) table%key = 'example' - call set_value(table, 'vers', 'abc') + call set_value(table, 'v', 'abc') call new_dependency(dependency, table, error=error) @@ -398,7 +398,7 @@ subroutine test_dependency_redundant_vers(error) call new_table(table) table%key = 'example' - call set_value(table, 'vers', '0.0.0') + call set_value(table, 'v', '0.0.0') call set_value(table, 'path', 'abc') call new_dependency(dependency, table, error=error) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 12a6d0869c..08b0529340 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -306,7 +306,7 @@ subroutine test_version_not_found_in_registry(error) call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') - call set_value(table, 'vers', '0.1.0') + call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return @@ -345,7 +345,7 @@ subroutine test_version_found_in_registry(error) call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') - call set_value(table, 'vers', '0.1.0') + call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index ffba27a5ca..c3b8f5fb2a 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -196,7 +196,7 @@ subroutine has_existent_path_to_registry(error) call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=10) :: '[registry]', 'path="."']) + & [character(len=10) :: '[registry]', 'path="."']) call setup_global_settings(global_settings, error) if (allocated(error)) return @@ -300,7 +300,7 @@ subroutine canonical_path_to_registry(error) call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) + & [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) call setup_global_settings(global_settings, error) if (allocated(error)) return @@ -347,7 +347,7 @@ subroutine has_url_to_registry(error) if (allocated(global_settings%registry_settings%path)) then call test_failed(error, "Path shouldn't be allocated: '" & - //global_settings%registry_settings%path//"'") + & //global_settings%registry_settings%path//"'") return end if From dcce05f8ae1009dbe0750290833d746a5855504f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 9 Feb 2023 00:45:15 +0100 Subject: [PATCH 091/799] Add tests for parsing the cache location --- src/fpm_settings.f90 | 10 +- test/fpm_test/test_settings.f90 | 194 +++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 13 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 002b6e332a..fc96bdc04b 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -76,9 +76,8 @@ subroutine get_global_settings(global_settings, error) ! Use default file name. global_settings%config_file_name = 'config.toml' - ! Return if path or file doesn't exist. - if (.not. exists(global_settings%path_to_config_folder) & - .or. .not. exists(global_settings%full_path())) return + ! Return if config file doesn't exist. + if (.not. exists(global_settings%full_path())) return end if ! Load into TOML table. @@ -196,11 +195,6 @@ subroutine get_registry_settings(global_settings, table, error) & global_settings%registry_settings%cache_path, error) if (allocated(error)) return end if - ! Both path and cache_path not allocated, use default location for cache_path. - else if (.not. allocated(path)) then - cache_path = join_path(global_settings%path_to_config_folder, 'dependencies') - global_settings%registry_settings%cache_path = cache_path - if (.not. exists(cache_path)) call mkdir(cache_path) end if end subroutine get_registry_settings diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index c3b8f5fb2a..beeb07d0f0 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -34,7 +34,12 @@ subroutine collect_settings(tests) & new_unittest('relative-path-to-registry', relative_path_to_registry), & & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & & new_unittest('has-url-to-registry', has_url_to_registry), & - & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.) & + & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.), & + & new_unittest('has-both-path-and-cache-path', has_both_path_and_cache_path, should_fail=.true.), & + & new_unittest('abs-cache-path-no-dir', abs_cache_path_no_dir), & + & new_unittest('abs-cache-path-has-dir', abs_cache_path_has_dir), & + & new_unittest('rel-cache-path-no-dir', rel_cache_path_no_dir), & + & new_unittest('rel-cache-path-has-dir', rel_cache_path_has_dir) & ] end subroutine collect_settings @@ -223,16 +228,17 @@ subroutine absolute_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path + character(len=:), allocatable :: file_content call delete_tmp_folder call mkdir(tmp_folder) call get_absolute_path(tmp_folder, abs_path, error) - if (allocated(error)) return - call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=80) :: '[registry]', "path='"//abs_path//"'"]) + file_content = '[registry]'//new_line('a')//"path='"//abs_path//"'" + + call filewrite(join_path(tmp_folder, config_file_name), [file_content]) call setup_global_settings(global_settings, error) if (allocated(error)) return @@ -355,6 +361,11 @@ subroutine has_url_to_registry(error) call test_failed(error, 'Url not allocated') return end if + + if (global_settings%registry_settings%url /= 'http') then + call test_failed(error, 'Failed to parse url') + return + end if end subroutine subroutine has_both_path_and_url_to_registry(error) @@ -365,12 +376,185 @@ subroutine has_both_path_and_url_to_registry(error) call mkdir(tmp_folder) call filewrite(join_path(tmp_folder, config_file_name), & - [character(len=10) :: '[registry]', 'path="."', 'url="http"']) + & [character(len=10) :: '[registry]', 'path="."', 'url="http"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + subroutine has_both_path_and_cache_path(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + + call delete_tmp_folder + call mkdir(tmp_folder) + + call filewrite(join_path(tmp_folder, config_file_name), & + & [character(len=18) :: '[registry]', 'path="."', 'cache_path="cache"']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + ! Custom cache location defined via absolute path but directory doesn't exist. Create it. + subroutine abs_cache_path_no_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: file_content + character(len=:), allocatable :: abs_path + character(len=:), allocatable :: abs_path_to_cache + + call delete_tmp_folder + call mkdir(tmp_folder) + + call get_absolute_path(tmp_folder, abs_path, error) + if (allocated(error)) return + + abs_path_to_cache = join_path(abs_path, 'cache') + + file_content = '[registry]'//new_line('a')//"cache_path='"//abs_path_to_cache//"'" + call filewrite(join_path(tmp_folder, config_file_name), [file_content]) + + if (exists(abs_path_to_cache)) then + call test_failed(error, "Cache directory '"// & + & abs_path_to_cache//"' already exists.") + return + end if + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + + if (.not. exists(abs_path_to_cache)) then + call test_failed(error, "Cache directory '"//abs_path_to_cache//"' not created.") + return + end if + + if (global_settings%registry_settings%cache_path /= abs_path_to_cache) then + call test_failed(error, "Cache path '"//abs_path_to_cache//"' not registered.") + return + end if + + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + ! Custom cache location defined via absolute path for existing directory. + subroutine abs_cache_path_has_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: file_content + character(len=:), allocatable :: abs_path + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache')) + + call get_absolute_path(join_path(tmp_folder, 'cache'), abs_path, error) + if (allocated(error)) return + + file_content = '[registry]'//new_line('a')//"cache_path='"//abs_path//"'" + call filewrite(join_path(tmp_folder, config_file_name), [file_content]) + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + + if (.not. exists(abs_path)) then + call test_failed(error, "Cache directory '"//abs_path//"' not created.") + return + end if + + if (global_settings%registry_settings%cache_path /= abs_path) then + call test_failed(error, "Cache path '"//abs_path//"' not registered.") + return + end if + + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + ! Custom cache location defined via relative path but directory doesn't exist. Create it. + subroutine rel_cache_path_no_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: file_content + character(:), allocatable :: cache_path + character(:), allocatable :: abs_path + + call delete_tmp_folder + call mkdir(tmp_folder) + + cache_path = join_path(tmp_folder, 'cache') + + file_content = '[registry]'//new_line('a')//'cache_path="cache"' + call filewrite(join_path(tmp_folder, config_file_name), [file_content]) + + if (exists(cache_path)) then + call test_failed(error, "Cache directory '"//cache_path//"' already exists.") + return + end if + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + + if (.not. exists(cache_path)) then + call test_failed(error, "Cache directory '"//cache_path//"' not created.") + return + end if + + call get_absolute_path(cache_path, abs_path, error) + if (allocated(error)) return + + if (global_settings%registry_settings%cache_path /= abs_path) then + call test_failed(error, "Cache path '"//cache_path//"' not registered.") + return + end if + + call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine + + ! Custom cache location defined via relative path for existing directory. + subroutine rel_cache_path_has_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: file_content + character(len=:), allocatable :: cache_path + character(len=:), allocatable :: abs_path + + call delete_tmp_folder + + cache_path = join_path(tmp_folder, 'cache') + call mkdir(cache_path) + + file_content = '[registry]'//new_line('a')//'cache_path="cache"' + call filewrite(join_path(tmp_folder, config_file_name), [file_content]) call setup_global_settings(global_settings, error) if (allocated(error)) return call get_global_settings(global_settings, error) + + if (.not. exists(cache_path)) then + call test_failed(error, "Cache directory '"//cache_path//"' not created.") + return + end if + + call get_absolute_path(cache_path, abs_path, error) + if (allocated(error)) return + + if (global_settings%registry_settings%cache_path /= abs_path) then + call test_failed(error, "Cache path '"//cache_path//"' not registered.") + return + end if + call os_delete_dir(os_is_unix(), tmp_folder) end subroutine From 9381880a1873268a122f506c94ed1f3b40a6576d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 9 Feb 2023 14:38:46 +0100 Subject: [PATCH 092/799] Use procedure --- test/fpm_test/test_settings.f90 | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index beeb07d0f0..4cd5ea11c2 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -101,7 +101,7 @@ subroutine empty_file(error) call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (allocated(error)) return @@ -125,7 +125,7 @@ subroutine empty_registry_table(error) call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (allocated(error)) return @@ -158,7 +158,7 @@ subroutine wrong_key(error) if (allocated(error)) return call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine subroutine wrong_type(error) @@ -174,7 +174,7 @@ subroutine wrong_type(error) if (allocated(error)) return call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine subroutine has_non_existent_path_to_registry(error) @@ -190,7 +190,7 @@ subroutine has_non_existent_path_to_registry(error) if (allocated(error)) return call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine subroutine has_existent_path_to_registry(error) @@ -208,7 +208,7 @@ subroutine has_existent_path_to_registry(error) call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (allocated(error)) return @@ -245,7 +245,7 @@ subroutine absolute_path_to_registry(error) call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (allocated(error)) return @@ -282,7 +282,7 @@ subroutine relative_path_to_registry(error) call get_absolute_path(tmp_folder, abs_path, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (allocated(error)) return @@ -315,7 +315,7 @@ subroutine canonical_path_to_registry(error) call get_absolute_path(tmp_folder, abs_path, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (allocated(error)) return @@ -344,7 +344,7 @@ subroutine has_url_to_registry(error) call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder if (.not. allocated(global_settings%registry_settings)) then call test_failed(error, 'Registry settings not allocated') @@ -382,7 +382,7 @@ subroutine has_both_path_and_url_to_registry(error) if (allocated(error)) return call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine subroutine has_both_path_and_cache_path(error) @@ -399,7 +399,7 @@ subroutine has_both_path_and_cache_path(error) if (allocated(error)) return call get_global_settings(global_settings, error) - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine ! Custom cache location defined via absolute path but directory doesn't exist. Create it. @@ -442,7 +442,7 @@ subroutine abs_cache_path_no_dir(error) return end if - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine ! Custom cache location defined via absolute path for existing directory. @@ -476,7 +476,7 @@ subroutine abs_cache_path_has_dir(error) return end if - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine ! Custom cache location defined via relative path but directory doesn't exist. Create it. @@ -518,7 +518,7 @@ subroutine rel_cache_path_no_dir(error) return end if - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine ! Custom cache location defined via relative path for existing directory. @@ -555,7 +555,7 @@ subroutine rel_cache_path_has_dir(error) return end if - call os_delete_dir(os_is_unix(), tmp_folder) + call delete_tmp_folder end subroutine end module test_settings From 53bbaefa42b32e7432696741d02bb315e475dbbf Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 9 Feb 2023 19:27:02 +0100 Subject: [PATCH 093/799] Improve and add tests for global_settings --- test/fpm_test/test_settings.f90 | 69 ++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 4cd5ea11c2..864787d090 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -1,7 +1,7 @@ module test_settings use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_settings, only: fpm_global_settings, get_global_settings - use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists + use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists, get_local_prefix use fpm_environment, only: os_is_unix use fpm_toml, only: new_table use fpm_os, only: get_absolute_path, get_current_directory @@ -25,6 +25,8 @@ subroutine collect_settings(tests) & new_unittest('no-folder', no_folder, should_fail=.true.), & & new_unittest('no-file', no_file, should_fail=.true.), & & new_unittest('empty-file', empty_file), & + & new_unittest('default-config-settings', default_config_settings), & + & new_unittest('error-reading-table', error_reading_table, should_fail=.true.), & & new_unittest('empty-registry-table', empty_registry_table), & & new_unittest('wrong-key', wrong_key, should_fail=.true.), & & new_unittest('wrong-type', wrong_type, should_fail=.true.), & @@ -86,11 +88,13 @@ subroutine no_file(error) call get_global_settings(global_settings, error) end subroutine no_file - !> Config file exists and working directory is set. + !> Config file exists and the path to that file is set. subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + character(:), allocatable :: cwd + call delete_tmp_folder call mkdir(tmp_folder) @@ -105,12 +109,73 @@ subroutine empty_file(error) if (allocated(error)) return + call get_current_directory(cwd, error) + if (allocated(error)) return + + if (global_settings%path_to_config_folder /= join_path(cwd, tmp_folder)) then + call test_failed(error, "global_settings%path_to_config_folder not set correctly :'" & + & //global_settings%path_to_config_folder//"'") + return + end if + if (allocated(global_settings%registry_settings)) then call test_failed(error, 'global_settings%registry_settings should not be allocated') return end if end subroutine empty_file + !> No custom path and config file specified, use default path and file name. + subroutine default_config_settings(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + + character(:), allocatable :: default_path + + call delete_tmp_folder + + call get_global_settings(global_settings, error) + if (allocated(error)) return + + if (os_is_unix()) then + default_path = join_path(get_local_prefix(), 'share', 'fpm') + else + default_path = join_path(get_local_prefix(), 'fpm') + end if + + if (global_settings%path_to_config_folder /= default_path) then + call test_failed(error, "Path to config folder not set correctly :'"//global_settings%config_file_name//"'") + return + end if + + if (global_settings%config_file_name /= 'config.toml') then + call test_failed(error, "Config file name not set correctly :'"//global_settings%config_file_name//"'") + return + end if + end subroutine default_config_settings + + !> Invalid TOML file. + subroutine error_reading_table(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + + call delete_tmp_folder + call mkdir(tmp_folder) + + call filewrite(join_path(tmp_folder, config_file_name), ['[']) + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call get_global_settings(global_settings, error) + + call delete_tmp_folder + + if (allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings should not be allocated') + return + end if + end subroutine error_reading_table + subroutine empty_registry_table(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings From 576f2001499f51266e5e4dcc843f63f23f84ea3d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Feb 2023 01:32:11 +0100 Subject: [PATCH 094/799] Improve get_registry_settings and its tests --- src/fpm_settings.f90 | 42 ++--- test/fpm_test/test_settings.f90 | 292 +++++++++++++++++++------------- 2 files changed, 198 insertions(+), 136 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index fc96bdc04b..5ba5be3dd9 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -8,7 +8,7 @@ module fpm_settings convert_to_absolute_path implicit none private - public :: fpm_global_settings, get_global_settings + public :: fpm_global_settings, get_global_settings, get_registry_settings type :: fpm_global_settings !> Path to the global config file excluding the file name. @@ -48,6 +48,9 @@ subroutine get_global_settings(global_settings, error) !> Error parsing to TOML table. type(toml_error), allocatable :: parse_error + type(toml_table), pointer :: registry_table + integer :: stat + ! Use custom path to the config file if it was specified. if (global_settings%has_custom_location()) then ! Throw error if folder doesn't exist. @@ -89,18 +92,28 @@ subroutine get_global_settings(global_settings, error) return end if - ! Read registry subtable. - call get_registry_settings(global_settings, table, error) + call get_value(table, 'registry', registry_table, requested=.false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading registry from config file '"// & + & global_settings%full_path()//"'.") + return + end if + + ! A registry table was found. + if (associated(registry_table)) then + call get_registry_settings(global_settings, registry_table, error) + return + end if end subroutine get_global_settings !> Get settings from the [registry] table in the global config file. subroutine get_registry_settings(global_settings, table, error) type(fpm_global_settings), intent(inout) :: global_settings - type(toml_table), intent(inout) :: table + type(toml_table), pointer, intent(in) :: table type(error_t), allocatable, intent(out) :: error - type(toml_table), pointer :: child character(:), allocatable :: path, url, cache_path integer :: stat @@ -111,23 +124,12 @@ subroutine get_registry_settings(global_settings, table, error) & 'cache_path' & & ] - call get_value(table, 'registry', child, requested=.false., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry from config file '"// & - & global_settings%full_path()//"'.") - return - end if - - ! Quietly return if no registry table was found. - if (.not. associated(child)) return - - call check_keys(child, valid_keys, error) + call check_keys(table, valid_keys, error) if (allocated(error)) return allocate (global_settings%registry_settings) - call get_value(child, 'path', path, stat=stat) + call get_value(table, 'path', path, stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Error reading registry path: '"//path//"'.") @@ -152,7 +154,7 @@ subroutine get_registry_settings(global_settings, table, error) end if end if - call get_value(child, 'url', url, stat=stat) + call get_value(table, 'url', url, stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Error reading registry url: '"//url//"'.") @@ -169,7 +171,7 @@ subroutine get_registry_settings(global_settings, table, error) global_settings%registry_settings%url = url end if - call get_value(child, 'cache_path', cache_path, stat=stat) + call get_value(table, 'cache_path', cache_path, stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'.") diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 864787d090..e08889e3c4 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -1,9 +1,9 @@ module test_settings use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_settings, only: fpm_global_settings, get_global_settings + use fpm_settings, only: fpm_global_settings, get_global_settings, get_registry_settings use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists, get_local_prefix use fpm_environment, only: os_is_unix - use fpm_toml, only: new_table + use fpm_toml, only: toml_table, new_table, add_table, set_value use fpm_os, only: get_absolute_path, get_current_directory implicit none @@ -28,12 +28,13 @@ subroutine collect_settings(tests) & new_unittest('default-config-settings', default_config_settings), & & new_unittest('error-reading-table', error_reading_table, should_fail=.true.), & & new_unittest('empty-registry-table', empty_registry_table), & - & new_unittest('wrong-key', wrong_key, should_fail=.true.), & - & new_unittest('wrong-type', wrong_type, should_fail=.true.), & + & new_unittest('invalid-key', invalid_key, should_fail=.true.), & + & new_unittest('invalid-type', invalid_type, should_fail=.true.), & & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & & new_unittest('relative-path-to-registry', relative_path_to_registry), & + & new_unittest('relative-path-to-registry-file-read', relative_path_to_registry_file_read), & & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & & new_unittest('has-url-to-registry', has_url_to_registry), & & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.), & @@ -72,7 +73,7 @@ subroutine no_folder(error) call setup_global_settings(global_settings, error) if (allocated(error)) return call get_global_settings(global_settings, error) - end subroutine no_folder + end subroutine !> Throw error when custom path to config file was entered but no file exists. subroutine no_file(error) @@ -86,7 +87,7 @@ subroutine no_file(error) if (allocated(error)) return call get_global_settings(global_settings, error) - end subroutine no_file + end subroutine !> Config file exists and the path to that file is set. subroutine empty_file(error) @@ -122,7 +123,7 @@ subroutine empty_file(error) call test_failed(error, 'global_settings%registry_settings should not be allocated') return end if - end subroutine empty_file + end subroutine !> No custom path and config file specified, use default path and file name. subroutine default_config_settings(error) @@ -151,7 +152,7 @@ subroutine default_config_settings(error) call test_failed(error, "Config file name not set correctly :'"//global_settings%config_file_name//"'") return end if - end subroutine default_config_settings + end subroutine !> Invalid TOML file. subroutine error_reading_table(error) @@ -174,28 +175,23 @@ subroutine error_reading_table(error) call test_failed(error, 'Registry settings should not be allocated') return end if - end subroutine error_reading_table + end subroutine subroutine empty_registry_table(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat - call delete_tmp_folder - call mkdir(tmp_folder) - - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]']) - - call setup_global_settings(global_settings, error) - if (allocated(error)) return - - call get_global_settings(global_settings, error) - - call delete_tmp_folder + call new_table(table) + call add_table(table, 'registry', child, stat) + call get_registry_settings(global_settings, child, error) if (allocated(error)) return if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'global_settings%registry_settings not allocated') + call test_failed(error, 'Registry settings not allocated') return end if @@ -210,75 +206,80 @@ subroutine empty_registry_table(error) end if end subroutine - subroutine wrong_key(error) + subroutine invalid_key(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat - call delete_tmp_folder - call mkdir(tmp_folder) - - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'abcd="abc"']) ! Invalid key - - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'invalid_key', 'abc', stat) - call get_global_settings(global_settings, error) - call delete_tmp_folder + call get_registry_settings(global_settings, child, error) end subroutine - subroutine wrong_type(error) + subroutine invalid_type(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat - call delete_tmp_folder - call mkdir(tmp_folder) - - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path=12345']) ! Value not a string - - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', 42, stat) - call get_global_settings(global_settings, error) - call delete_tmp_folder + call get_registry_settings(global_settings, child, error) end subroutine subroutine has_non_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat - call delete_tmp_folder - call mkdir(tmp_folder) - - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', 'nonexistent_path', stat) - call setup_global_settings(global_settings, error) - if (allocated(error)) return - - call get_global_settings(global_settings, error) - call delete_tmp_folder + call get_registry_settings(global_settings, child, error) end subroutine subroutine has_existent_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + character(:), allocatable :: cwd + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=10) :: '[registry]', 'path="."']) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', '.', stat) + + call get_registry_settings(global_settings, child, error) + + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if call delete_tmp_folder + call get_current_directory(cwd, error) if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings%path)) then - call test_failed(error, 'Path not allocated') + if (global_settings%registry_settings%path /= join_path(cwd, tmp_folder)) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") return end if @@ -293,7 +294,9 @@ subroutine absolute_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path - character(len=:), allocatable :: file_content + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -301,14 +304,14 @@ subroutine absolute_path_to_registry(error) call get_absolute_path(tmp_folder, abs_path, error) if (allocated(error)) return - file_content = '[registry]'//new_line('a')//"path='"//abs_path//"'" - - call filewrite(join_path(tmp_folder, config_file_name), [file_content]) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', abs_path, stat) + + call get_registry_settings(global_settings, child, error) call delete_tmp_folder @@ -334,6 +337,44 @@ subroutine relative_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'abc')) + + call setup_global_settings(global_settings, error) + if (allocated(error)) return + + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', 'abc', stat) + + call get_registry_settings(global_settings, child, error) + + call get_absolute_path(tmp_folder, abs_path, error) + + call delete_tmp_folder + + if (allocated(error)) return + + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if + + if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine + + ! Test that the registry path is set correctly when the path is written to and read from a config file. + subroutine relative_path_to_registry_file_read(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path call delete_tmp_folder call mkdir(join_path(tmp_folder, 'abc')) @@ -366,17 +407,21 @@ subroutine canonical_path_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=20) :: '[registry]', "path='"//join_path('..', 'tmp')//"'"]) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', join_path('..', tmp_folder), stat) + + call get_registry_settings(global_settings, child, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -398,16 +443,21 @@ subroutine canonical_path_to_registry(error) subroutine has_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'url="http"']) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'url', 'http', stat) + + call get_registry_settings(global_settings, child, error) call delete_tmp_folder @@ -436,34 +486,46 @@ subroutine has_url_to_registry(error) subroutine has_both_path_and_url_to_registry(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=10) :: '[registry]', 'path="."', 'url="http"']) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', '.', stat) + call set_value(child, 'url', 'http', stat) + + call get_registry_settings(global_settings, child, error) + call delete_tmp_folder end subroutine subroutine has_both_path_and_cache_path(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=18) :: '[registry]', 'path="."', 'cache_path="cache"']) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'path', '.', stat) + call set_value(child, 'cache_path', 'cache', stat) + + call get_registry_settings(global_settings, child, error) + call delete_tmp_folder end subroutine @@ -471,9 +533,10 @@ subroutine has_both_path_and_cache_path(error) subroutine abs_cache_path_no_dir(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: file_content - character(len=:), allocatable :: abs_path - character(len=:), allocatable :: abs_path_to_cache + character(len=:), allocatable :: abs_path, abs_path_to_cache + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -483,19 +546,14 @@ subroutine abs_cache_path_no_dir(error) abs_path_to_cache = join_path(abs_path, 'cache') - file_content = '[registry]'//new_line('a')//"cache_path='"//abs_path_to_cache//"'" - call filewrite(join_path(tmp_folder, config_file_name), [file_content]) - - if (exists(abs_path_to_cache)) then - call test_failed(error, "Cache directory '"// & - & abs_path_to_cache//"' already exists.") - return - end if - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'cache_path', abs_path_to_cache, stat) + + call get_registry_settings(global_settings, child, error) if (.not. exists(abs_path_to_cache)) then call test_failed(error, "Cache directory '"//abs_path_to_cache//"' not created.") @@ -514,8 +572,10 @@ subroutine abs_cache_path_no_dir(error) subroutine abs_cache_path_has_dir(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: file_content character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache')) @@ -523,13 +583,14 @@ subroutine abs_cache_path_has_dir(error) call get_absolute_path(join_path(tmp_folder, 'cache'), abs_path, error) if (allocated(error)) return - file_content = '[registry]'//new_line('a')//"cache_path='"//abs_path//"'" - call filewrite(join_path(tmp_folder, config_file_name), [file_content]) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'cache_path', abs_path, stat) + + call get_registry_settings(global_settings, child, error) if (.not. exists(abs_path)) then call test_failed(error, "Cache directory '"//abs_path//"' not created.") @@ -548,27 +609,24 @@ subroutine abs_cache_path_has_dir(error) subroutine rel_cache_path_no_dir(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: file_content - character(:), allocatable :: cache_path - character(:), allocatable :: abs_path + character(:), allocatable :: cache_path, abs_path + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder call mkdir(tmp_folder) - cache_path = join_path(tmp_folder, 'cache') - - file_content = '[registry]'//new_line('a')//'cache_path="cache"' - call filewrite(join_path(tmp_folder, config_file_name), [file_content]) - - if (exists(cache_path)) then - call test_failed(error, "Cache directory '"//cache_path//"' already exists.") - return - end if - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'cache_path', 'cache', stat) + + call get_registry_settings(global_settings, child, error) + + cache_path = join_path(tmp_folder, 'cache') if (.not. exists(cache_path)) then call test_failed(error, "Cache directory '"//cache_path//"' not created.") @@ -590,22 +648,24 @@ subroutine rel_cache_path_no_dir(error) subroutine rel_cache_path_has_dir(error) type(error_t), allocatable, intent(out) :: error type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: file_content - character(len=:), allocatable :: cache_path - character(len=:), allocatable :: abs_path + character(len=:), allocatable :: cache_path, abs_path + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat call delete_tmp_folder cache_path = join_path(tmp_folder, 'cache') call mkdir(cache_path) - file_content = '[registry]'//new_line('a')//'cache_path="cache"' - call filewrite(join_path(tmp_folder, config_file_name), [file_content]) - call setup_global_settings(global_settings, error) if (allocated(error)) return - call get_global_settings(global_settings, error) + call new_table(table) + call add_table(table, 'registry', child, stat) + call set_value(child, 'cache_path', 'cache', stat) + + call get_registry_settings(global_settings, child, error) if (.not. exists(cache_path)) then call test_failed(error, "Cache directory '"//cache_path//"' not created.") From b7ad9d63996f321a6d2e16f7a0e8a255fb2f95b5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Feb 2023 01:45:57 +0100 Subject: [PATCH 095/799] Use target instead of pointer and unify naming --- src/fpm/manifest/dependency.f90 | 2 +- src/fpm_settings.f90 | 2 +- test/fpm_test/test_manifest.f90 | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 3114df7bb2..63cf7e2bd5 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -193,7 +193,7 @@ subroutine check(table, error) end if if (table%has_key('v') .and. (table%has_key('path') .or. table%has_key('git'))) then - call syntax_error(error, "Dependency '"//name//"' cannot have both vers and git/path entries") + call syntax_error(error, "Dependency '"//name//"' cannot have both v and git/path entries") return end if diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 5ba5be3dd9..96ae4feb69 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -111,7 +111,7 @@ end subroutine get_global_settings !> Get settings from the [registry] table in the global config file. subroutine get_registry_settings(global_settings, table, error) type(fpm_global_settings), intent(inout) :: global_settings - type(toml_table), pointer, intent(in) :: table + type(toml_table), target, intent(inout) :: table type(error_t), allocatable, intent(out) :: error character(:), allocatable :: path, url, cache_path diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 82ee61a4ae..2590c3016e 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -29,7 +29,7 @@ subroutine collect_manifest(tests) & new_unittest("dependency-gitconflict", test_dependency_gitconflict, should_fail=.true.), & & new_unittest("dependency-invalid-git", test_dependency_invalid_git, should_fail=.true.), & & new_unittest("dependency-no-namespace", test_dependency_no_namespace, should_fail=.true.), & - & new_unittest("dependency-redundant-vers", test_dependency_redundant_vers, should_fail=.true.), & + & new_unittest("dependency-redundant-v", test_dependency_redundant_v, should_fail=.true.), & & new_unittest("dependency-wrongkey", test_dependency_wrongkey, should_fail=.true.), & & new_unittest("dependencies-empty", test_dependencies_empty), & & new_unittest("dependencies-typeerror", test_dependencies_typeerror, should_fail=.true.), & @@ -387,7 +387,7 @@ subroutine test_dependency_no_namespace(error) end subroutine test_dependency_no_namespace !> Do not specify version with a git or path dependency - subroutine test_dependency_redundant_vers(error) + subroutine test_dependency_redundant_v(error) use fpm_manifest_dependency use fpm_toml, only : new_table, toml_table, set_value @@ -403,7 +403,7 @@ subroutine test_dependency_redundant_vers(error) call new_dependency(dependency, table, error=error) - end subroutine test_dependency_redundant_vers + end subroutine test_dependency_redundant_v !> Try to create a dependency with conflicting entries From 9b950c4eb719a9e5da1e72b887e00b9eb727f262 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Feb 2023 14:10:04 +0100 Subject: [PATCH 096/799] Remove target --- src/fpm_settings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 96ae4feb69..6f8e3b648f 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -111,7 +111,7 @@ end subroutine get_global_settings !> Get settings from the [registry] table in the global config file. subroutine get_registry_settings(global_settings, table, error) type(fpm_global_settings), intent(inout) :: global_settings - type(toml_table), target, intent(inout) :: table + type(toml_table), intent(inout) :: table type(error_t), allocatable, intent(out) :: error character(:), allocatable :: path, url, cache_path From 70c8f267151eb6dfbfcb08f2f6da22a84c11b8da Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Feb 2023 15:06:45 +0100 Subject: [PATCH 097/799] Swap arguments --- src/fpm_settings.f90 | 11 +++++++---- test/fpm_test/test_settings.f90 | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 6f8e3b648f..dbd6e6ce53 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -102,16 +102,19 @@ subroutine get_global_settings(global_settings, error) ! A registry table was found. if (associated(registry_table)) then - call get_registry_settings(global_settings, registry_table, error) + call get_registry_settings(registry_table, global_settings, error) return end if end subroutine get_global_settings - !> Get settings from the [registry] table in the global config file. - subroutine get_registry_settings(global_settings, table, error) - type(fpm_global_settings), intent(inout) :: global_settings + !> Read registry settings from the global config file. + subroutine get_registry_settings(table, global_settings, error) + !> The [registry] subtable from the global config file. type(toml_table), intent(inout) :: table + !> The global settings which can be filled with the registry settings. + type(fpm_global_settings), intent(inout) :: global_settings + !> Error handling. type(error_t), allocatable, intent(out) :: error character(:), allocatable :: path, url, cache_path diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index e08889e3c4..fc31fd3016 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -187,7 +187,7 @@ subroutine empty_registry_table(error) call new_table(table) call add_table(table, 'registry', child, stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) if (allocated(error)) return if (.not. allocated(global_settings%registry_settings)) then @@ -217,7 +217,7 @@ subroutine invalid_key(error) call add_table(table, 'registry', child, stat) call set_value(child, 'invalid_key', 'abc', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) end subroutine subroutine invalid_type(error) @@ -231,7 +231,7 @@ subroutine invalid_type(error) call add_table(table, 'registry', child, stat) call set_value(child, 'path', 42, stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) end subroutine subroutine has_non_existent_path_to_registry(error) @@ -245,7 +245,7 @@ subroutine has_non_existent_path_to_registry(error) call add_table(table, 'registry', child, stat) call set_value(child, 'path', 'nonexistent_path', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) end subroutine subroutine has_existent_path_to_registry(error) @@ -266,7 +266,7 @@ subroutine has_existent_path_to_registry(error) call add_table(table, 'registry', child, stat) call set_value(child, 'path', '.', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) if (.not. allocated(global_settings%registry_settings%path)) then call test_failed(error, 'Path not allocated') @@ -311,7 +311,7 @@ subroutine absolute_path_to_registry(error) call add_table(table, 'registry', child, stat) call set_value(child, 'path', abs_path, stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) call delete_tmp_folder @@ -351,7 +351,7 @@ subroutine relative_path_to_registry(error) call add_table(table, 'registry', child, stat) call set_value(child, 'path', 'abc', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -421,7 +421,7 @@ subroutine canonical_path_to_registry(error) call add_table(table, 'registry', child, stat) call set_value(child, 'path', join_path('..', tmp_folder), stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) call get_absolute_path(tmp_folder, abs_path, error) @@ -457,7 +457,7 @@ subroutine has_url_to_registry(error) call add_table(table, 'registry', child, stat) call set_value(child, 'url', 'http', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) call delete_tmp_folder @@ -501,7 +501,7 @@ subroutine has_both_path_and_url_to_registry(error) call set_value(child, 'path', '.', stat) call set_value(child, 'url', 'http', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) call delete_tmp_folder end subroutine @@ -524,7 +524,7 @@ subroutine has_both_path_and_cache_path(error) call set_value(child, 'path', '.', stat) call set_value(child, 'cache_path', 'cache', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) call delete_tmp_folder end subroutine @@ -553,7 +553,7 @@ subroutine abs_cache_path_no_dir(error) call add_table(table, 'registry', child, stat) call set_value(child, 'cache_path', abs_path_to_cache, stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) if (.not. exists(abs_path_to_cache)) then call test_failed(error, "Cache directory '"//abs_path_to_cache//"' not created.") @@ -590,7 +590,7 @@ subroutine abs_cache_path_has_dir(error) call add_table(table, 'registry', child, stat) call set_value(child, 'cache_path', abs_path, stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) if (.not. exists(abs_path)) then call test_failed(error, "Cache directory '"//abs_path//"' not created.") @@ -624,7 +624,7 @@ subroutine rel_cache_path_no_dir(error) call add_table(table, 'registry', child, stat) call set_value(child, 'cache_path', 'cache', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) cache_path = join_path(tmp_folder, 'cache') @@ -665,7 +665,7 @@ subroutine rel_cache_path_has_dir(error) call add_table(table, 'registry', child, stat) call set_value(child, 'cache_path', 'cache', stat) - call get_registry_settings(global_settings, child, error) + call get_registry_settings(child, global_settings, error) if (.not. exists(cache_path)) then call test_failed(error, "Cache directory '"//cache_path//"' not created.") From 076f4d0a70d4e48d332a4c36af7519015ee4ce2b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Feb 2023 15:11:13 +0100 Subject: [PATCH 098/799] Add target again --- src/fpm_settings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index dbd6e6ce53..a1045f1f34 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -111,7 +111,7 @@ end subroutine get_global_settings !> Read registry settings from the global config file. subroutine get_registry_settings(table, global_settings, error) !> The [registry] subtable from the global config file. - type(toml_table), intent(inout) :: table + type(toml_table), target, intent(inout) :: table !> The global settings which can be filled with the registry settings. type(fpm_global_settings), intent(inout) :: global_settings !> Error handling. From a892f3182e424c1c91b0648086631d78d4841a13 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Feb 2023 23:11:47 +0100 Subject: [PATCH 099/799] Change and improve get_from_registry and add tests --- src/fpm/dependency.f90 | 58 ++- test/fpm_test/test_package_dependencies.f90 | 410 ++++++++++++++++++-- test/fpm_test/test_settings.f90 | 77 ++-- 3 files changed, 441 insertions(+), 104 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 5bb5a20ca2..adde924eef 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -450,6 +450,7 @@ subroutine resolve_dependency(self, dependency, root, error) type(package_config_t) :: package character(len=:), allocatable :: manifest, proj_dir, revision + type(fpm_global_settings) :: global_settings logical :: fetch if (dependency%done) return @@ -466,7 +467,9 @@ subroutine resolve_dependency(self, dependency, root, error) if (allocated(error)) return end if else - call dependency%get_from_registry(proj_dir, error) + call get_global_settings(global_settings, error) + if (allocated(error)) return + call dependency%get_from_registry(proj_dir, global_settings, error) if (allocated(error)) return end if @@ -496,7 +499,7 @@ end subroutine resolve_dependency !> Get a dependency from the registry. Whether the dependency is fetched !> from a local, a custom remote or the official registry is determined !> by the global configuration settings. - subroutine get_from_registry(self, target_dir, error, global_settings) + subroutine get_from_registry(self, target_dir, global_settings, error) !> Instance of the dependency configuration. class(dependency_node_t), intent(in) :: self @@ -504,21 +507,39 @@ subroutine get_from_registry(self, target_dir, error, global_settings) !> The target directory of the dependency. character(:), allocatable, intent(out) :: target_dir + !> Global configuration settings. + type(fpm_global_settings), intent(inout) :: global_settings + !> Error handling. type(error_t), allocatable, intent(out) :: error - !> Global configuration settings. - type(fpm_global_settings), optional, intent(inout) :: global_settings - - call get_global_settings(global_settings, error) - if (allocated(error)) return - ! Registry settings found in the global config file. if (allocated(global_settings%registry_settings)) then if (allocated(global_settings%registry_settings%path)) then call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error) return end if + else + allocate (global_settings%registry_settings) + end if + + if (.not. allocated(global_settings%registry_settings%cache_path)) then + ! Use default cache path if it wasn't set in the global config file. + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, 'dependencies') + end if + + if (.not. exists(global_settings%registry_settings%cache_path)) then + call mkdir(global_settings%registry_settings%cache_path) + end if + + ! Check cache before downloading from remote registry when a specific version was requested. + if (allocated(self%requested_version)) then + if (exists(join_path(global_settings%registry_settings%cache_path, self%namespace, & + & self%name, self%requested_version%s()))) then + target_dir = join_path(global_settings%registry_settings%cache_path, self%namespace, & + & self%name, self%requested_version%s()) + return + end if end if call self%get_from_remote_registry(target_dir, global_settings, error) @@ -652,24 +673,25 @@ subroutine get_from_remote_registry(self, target_dir, global_settings, error) type(error_t), allocatable, intent(out) :: error type(string_t), allocatable :: files(:) - type(version_t), allocatable :: versions(:), version + type(version_t), allocatable :: versions(:) + type(version_t) :: version integer :: i ! Collect existing versions from the cache. - call list_files(global_settings%registry_settings%cache_path, files) + call list_files(join_path(global_settings%registry_settings%cache_path, self%namespace, self%name), files) if (size(files) > 0) then + allocate (versions(0)) do i = 1, size(files) - if (.not. is_dir(files(i)%s)) cycle - - call new_version(version, basename(files(i)%s), error) - if (allocated(error)) return - - versions = [versions, version] + if (is_dir(files(i)%s)) then + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + versions = [versions, version] + end if end do end if - ! Get new versions from the registry, sending existing versions. - ! Put them in the cache. + ! Send version to registry and receive requested package. + ! Put it in the cache. end subroutine get_from_remote_registry !> True if dependency is part of the tree diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 08b0529340..83b43d9262 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -2,13 +2,13 @@ module test_package_dependencies use fpm_filesystem, only: get_temp_filename use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_filesystem, only: is_dir, join_path, filewrite, mkdir, os_delete_dir + use fpm_filesystem, only: is_dir, join_path, filewrite, mkdir, os_delete_dir, exists use fpm_environment, only: os_is_unix use fpm_os, only: get_current_directory use fpm_dependency use fpm_manifest_dependency use fpm_toml - use fpm_settings, only: fpm_global_settings + use fpm_settings, only: fpm_global_settings, get_registry_settings implicit none private @@ -36,12 +36,17 @@ subroutine collect_package_dependencies(tests) & new_unittest("cache-dump-load", test_cache_dump_load), & & new_unittest("status-after-load", test_status), & & new_unittest("add-dependencies", test_add_dependencies), & - & new_unittest("registry-dir-not-found", test_registry_dir_not_found, should_fail=.true.), & - & new_unittest("no-versions-in-registry", test_no_versions_in_registry, should_fail=.true.), & - & new_unittest("version-not-found-in-registry", test_version_not_found_in_registry, should_fail=.true.), & - & new_unittest("version-found-in-registry", test_version_found_in_registry), & - & new_unittest("test-no-dir-in-registry", test_no_dir_in_registry, should_fail=.true.), & - & new_unittest("test-newest-version-in-registry", test_newest_version_in_registry) & + & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & + & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & + & new_unittest("version-not-found-in-registry", version_not_found_in_registry, should_fail=.true.), & + & new_unittest("found-in-registry", version_found_in_registry), & + & new_unittest("not-a-dir", not_a_dir, should_fail=.true.), & + & new_unittest("newest-version-in-registry", newest_version_in_registry), & + & new_unittest("check-default-cache-path-has-dir", check_default_cache_path_has_dir), & + & new_unittest("check-default-cache-path-no-dir", check_default_cache_path_no_dir), & + & new_unittest("version-found-in-default-cache", version_found_in_default_cache), & + & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & + & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache) & & ] end subroutine collect_package_dependencies @@ -221,13 +226,14 @@ subroutine test_add_dependencies(error) end subroutine test_add_dependencies - subroutine test_registry_dir_not_found(error) + subroutine registry_dir_not_found(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child call new_table(table) table%key = 'test-dep' @@ -239,8 +245,9 @@ subroutine test_registry_dir_not_found(error) call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache')) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=12) :: '[registry]', 'path="cache"']) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') ! Missing directories for namesapce and package name call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -248,7 +255,13 @@ subroutine test_registry_dir_not_found(error) return end if - call node%get_from_registry(target_dir, error, global_settings) + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder return @@ -256,15 +269,16 @@ subroutine test_registry_dir_not_found(error) call delete_tmp_folder - end subroutine test_registry_dir_not_found + end subroutine registry_dir_not_found - subroutine test_no_versions_in_registry(error) + subroutine no_versions_in_registry(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child call new_table(table) table%key = 'test-dep' @@ -276,8 +290,9 @@ subroutine test_no_versions_in_registry(error) call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=12) :: '[registry]', 'path="cache"']) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -285,7 +300,13 @@ subroutine test_no_versions_in_registry(error) return end if - call node%get_from_registry(target_dir, error, global_settings) + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder return @@ -293,15 +314,16 @@ subroutine test_no_versions_in_registry(error) call delete_tmp_folder - end subroutine test_no_versions_in_registry + end subroutine no_versions_in_registry - subroutine test_version_not_found_in_registry(error) + subroutine version_not_found_in_registry(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child call new_table(table) table%key = 'test-dep' @@ -315,8 +337,9 @@ subroutine test_version_not_found_in_registry(error) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.9')) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.1')) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=12) :: '[registry]', 'path="cache"']) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -324,7 +347,13 @@ subroutine test_version_not_found_in_registry(error) return end if - call node%get_from_registry(target_dir, error, global_settings) + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder return @@ -332,15 +361,16 @@ subroutine test_version_not_found_in_registry(error) call delete_tmp_folder - end subroutine test_version_not_found_in_registry + end subroutine version_not_found_in_registry - subroutine test_version_found_in_registry(error) + subroutine version_found_in_registry(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child call new_table(table) table%key = 'test-dep' @@ -355,8 +385,9 @@ subroutine test_version_found_in_registry(error) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.2.0')) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=12) :: '[registry]', 'path="cache"']) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -364,7 +395,13 @@ subroutine test_version_found_in_registry(error) return end if - call node%get_from_registry(target_dir, error, global_settings) + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder return @@ -384,15 +421,16 @@ subroutine test_version_found_in_registry(error) call delete_tmp_folder - end subroutine test_version_found_in_registry + end subroutine version_found_in_registry - subroutine test_no_dir_in_registry(error) + subroutine not_a_dir(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child call new_table(table) table%key = 'test-dep' @@ -403,10 +441,11 @@ subroutine test_no_dir_in_registry(error) call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) - call filewrite(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', 'abc'), ['abc']) + call filewrite(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'), ['']) ! File, not directory - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=12) :: '[registry]', 'path="cache"']) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -414,7 +453,13 @@ subroutine test_no_dir_in_registry(error) return end if - call node%get_from_registry(target_dir, error, global_settings) + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder return @@ -422,15 +467,16 @@ subroutine test_no_dir_in_registry(error) call delete_tmp_folder - end subroutine test_no_dir_in_registry + end subroutine not_a_dir - subroutine test_newest_version_in_registry(error) + subroutine newest_version_in_registry(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child call new_table(table) table%key = 'test-dep' @@ -444,8 +490,9 @@ subroutine test_newest_version_in_registry(error) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0')) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) - call filewrite(join_path(tmp_folder, config_file_name), & - & [character(len=12) :: '[registry]', 'path="cache"']) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -453,7 +500,13 @@ subroutine test_newest_version_in_registry(error) return end if - call node%get_from_registry(target_dir, error, global_settings) + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder return @@ -466,14 +519,290 @@ subroutine test_newest_version_in_registry(error) end if if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'))) then - call test_failed(error, 'target_dir not set correctly') + call test_failed(error, 'target_dir not set correctly: '//target_dir//"'") call delete_tmp_folder return end if call delete_tmp_folder - end subroutine test_newest_version_in_registry + end subroutine newest_version_in_registry + + !> No cache_path specified, use default cache path but folder exists already. + subroutine check_default_cache_path_has_dir(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'dependencies')) ! Dependency folder exists already + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call new_table(table) + call add_table(table, 'registry', child) + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (global_settings%registry_settings%cache_path /= & + & join_path(global_settings%path_to_config_folder, 'dependencies')) then + call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") + call delete_tmp_folder + return + end if + + if (.not. exists(global_settings%registry_settings%cache_path)) then + call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine check_default_cache_path_has_dir + + !> No cache_path specified, use default cache path and create folder. + subroutine check_default_cache_path_no_dir(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(tmp_folder) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call new_table(table) + call add_table(table, 'registry', child) + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (global_settings%registry_settings%cache_path /= & + & join_path(global_settings%path_to_config_folder, 'dependencies')) then + call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") + call delete_tmp_folder + return + end if + + if (.not. exists(global_settings%registry_settings%cache_path)) then + call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine check_default_cache_path_no_dir + + subroutine version_found_in_default_cache(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '2.3.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0')) + + call new_table(table) + call add_table(table, 'registry', child) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0'))) then + call test_failed(error, "Target directory not correctly set: '"//target_dir//"'") + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine version_found_in_default_cache + + subroutine no_version_in_default_cache(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '2.3.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(tmp_folder) ! Dependencies folder doesn't exist + + call new_table(table) + call add_table(table, 'registry', child) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (allocated(target_dir)) then + call test_failed(error, 'Target directory should not be set') + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine no_version_in_default_cache + + subroutine other_versions_in_default_cache(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '2.3.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.1.0')) + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '9.1.0')) + + call new_table(table) + call add_table(table, 'registry', child) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder + return + end if + + if (allocated(target_dir)) then + call test_failed(error, 'Target directory should not be set') + call delete_tmp_folder + return + end if + + call delete_tmp_folder + + end subroutine other_versions_in_default_cache !> Resolve a single dependency node subroutine resolve_dependency_once(self, dependency, root, error) @@ -490,6 +819,7 @@ subroutine resolve_dependency_once(self, dependency, root, error) call test_failed(error, "Should only visit this node once") return end if + dependency%done = .true. end subroutine resolve_dependency_once diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index fc31fd3016..87f734dd0c 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -182,10 +182,9 @@ subroutine empty_registry_table(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call new_table(table) - call add_table(table, 'registry', child, stat) + call add_table(table, 'registry', child) call get_registry_settings(child, global_settings, error) if (allocated(error)) return @@ -211,11 +210,10 @@ subroutine invalid_key(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'invalid_key', 'abc', stat) + call add_table(table, 'registry', child) + call set_value(child, 'invalid_key', 'abc') call get_registry_settings(child, global_settings, error) end subroutine @@ -225,11 +223,10 @@ subroutine invalid_type(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', 42, stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', 42) call get_registry_settings(child, global_settings, error) end subroutine @@ -239,11 +236,10 @@ subroutine has_non_existent_path_to_registry(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', 'nonexistent_path', stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'nonexistent_path') call get_registry_settings(child, global_settings, error) end subroutine @@ -254,7 +250,6 @@ subroutine has_existent_path_to_registry(error) type(toml_table) :: table type(toml_table), pointer :: child character(:), allocatable :: cwd - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -263,8 +258,8 @@ subroutine has_existent_path_to_registry(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', '.', stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', '.') call get_registry_settings(child, global_settings, error) @@ -296,7 +291,6 @@ subroutine absolute_path_to_registry(error) character(len=:), allocatable :: abs_path type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -308,8 +302,8 @@ subroutine absolute_path_to_registry(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', abs_path, stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', abs_path) call get_registry_settings(child, global_settings, error) @@ -339,7 +333,6 @@ subroutine relative_path_to_registry(error) character(len=:), allocatable :: abs_path type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(join_path(tmp_folder, 'abc')) @@ -348,8 +341,8 @@ subroutine relative_path_to_registry(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', 'abc', stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'abc') call get_registry_settings(child, global_settings, error) @@ -409,7 +402,6 @@ subroutine canonical_path_to_registry(error) character(len=:), allocatable :: abs_path type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -418,8 +410,8 @@ subroutine canonical_path_to_registry(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', join_path('..', tmp_folder), stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', join_path('..', tmp_folder)) call get_registry_settings(child, global_settings, error) @@ -445,7 +437,6 @@ subroutine has_url_to_registry(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -454,8 +445,8 @@ subroutine has_url_to_registry(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'url', 'http', stat) + call add_table(table, 'registry', child) + call set_value(child, 'url', 'http') call get_registry_settings(child, global_settings, error) @@ -488,7 +479,6 @@ subroutine has_both_path_and_url_to_registry(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -497,9 +487,9 @@ subroutine has_both_path_and_url_to_registry(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', '.', stat) - call set_value(child, 'url', 'http', stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', '.') + call set_value(child, 'url', 'http') call get_registry_settings(child, global_settings, error) @@ -511,7 +501,6 @@ subroutine has_both_path_and_cache_path(error) type(fpm_global_settings) :: global_settings type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -520,9 +509,9 @@ subroutine has_both_path_and_cache_path(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'path', '.', stat) - call set_value(child, 'cache_path', 'cache', stat) + call add_table(table, 'registry', child) + call set_value(child, 'path', '.') + call set_value(child, 'cache_path', 'cache') call get_registry_settings(child, global_settings, error) @@ -536,7 +525,6 @@ subroutine abs_cache_path_no_dir(error) character(len=:), allocatable :: abs_path, abs_path_to_cache type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -550,8 +538,8 @@ subroutine abs_cache_path_no_dir(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'cache_path', abs_path_to_cache, stat) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', abs_path_to_cache) call get_registry_settings(child, global_settings, error) @@ -575,7 +563,6 @@ subroutine abs_cache_path_has_dir(error) character(len=:), allocatable :: abs_path type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache')) @@ -587,8 +574,8 @@ subroutine abs_cache_path_has_dir(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'cache_path', abs_path, stat) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', abs_path) call get_registry_settings(child, global_settings, error) @@ -612,7 +599,6 @@ subroutine rel_cache_path_no_dir(error) character(:), allocatable :: cache_path, abs_path type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder call mkdir(tmp_folder) @@ -621,8 +607,8 @@ subroutine rel_cache_path_no_dir(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'cache_path', 'cache', stat) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', 'cache') call get_registry_settings(child, global_settings, error) @@ -651,7 +637,6 @@ subroutine rel_cache_path_has_dir(error) character(len=:), allocatable :: cache_path, abs_path type(toml_table) :: table type(toml_table), pointer :: child - integer :: stat call delete_tmp_folder @@ -662,8 +647,8 @@ subroutine rel_cache_path_has_dir(error) if (allocated(error)) return call new_table(table) - call add_table(table, 'registry', child, stat) - call set_value(child, 'cache_path', 'cache', stat) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', 'cache') call get_registry_settings(child, global_settings, error) From f9b88321681b44eee1506ee2376c629c09a5eab6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 17 Feb 2023 19:48:09 +0100 Subject: [PATCH 100/799] testing --- .gitignore | 3 +++ fpm.toml | 2 +- src/fpm.f90 | 12 +++++++++++- src/fpm/dependency.f90 | 28 ++++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9169294354..794667bf94 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ build/* # Visual Studio Code .vscode/ + +# CodeBlocks +project/ diff --git a/fpm.toml b/fpm.toml index 9c694cab41..25693520d6 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.7.0" +version = "0.7.1" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/src/fpm.f90 b/src/fpm.f90 index b9c0d2a874..19c9cbfbf0 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -60,6 +60,16 @@ subroutine build_model(model, settings, package, error) call model%deps%add(package, error) if (allocated(error)) return + do i = 1, model%deps%ndep + + if (model%deps%dep(i)%update) then + print *, ' Updating model dependency ',model%deps%dep(i)%name,' ...' + call model%deps%update(model%deps%dep(i)%name,error) + if (allocated(error)) return + end if + + end do + ! build/ directory should now exist if (.not.exists("build/.gitignore")) then call filewrite(join_path("build", ".gitignore"),["*"]) @@ -107,7 +117,7 @@ subroutine build_model(model, settings, package, error) model%packages(i)%name = dependency%name call package%version%to_string(version) model%packages(i)%version = version - + if (allocated(dependency%preprocess)) then do j = 1, size(dependency%preprocess) if (dependency%preprocess(j)%name == "cpp") then diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index bd85b6f014..0b14d24669 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -191,7 +191,7 @@ subroutine new_dependency_tree(self, verbosity, cache) end subroutine new_dependency_tree !> Create a new dependency node from a configuration - pure subroutine new_dependency_node(self, dependency, version, proj_dir, update) + subroutine new_dependency_node(self, dependency, version, proj_dir, update) !> Instance of the dependency node type(dependency_node_t), intent(out) :: self !> Dependency configuration data @@ -217,6 +217,8 @@ pure subroutine new_dependency_node(self, dependency, version, proj_dir, update) self%update = update end if + print *, 'new node from self=',self%name,' dep=',dependency%name, 'update=',update + end subroutine new_dependency_node !> Add project dependencies, each depth level after each other. @@ -357,7 +359,7 @@ subroutine add_dependencies(self, dependency, error) end subroutine add_dependencies !> Add a single dependency to the dependency tree - pure subroutine add_dependency(self, dependency, error) + subroutine add_dependency(self, dependency, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> Dependency configuration to add @@ -400,6 +402,7 @@ subroutine update_dependency(self, name, error) if (self%verbosity > 1) then write(self%unit, out_fmt) "Update:", dep%name end if + write(self%unit, out_fmt) "Update:", dep%name proj_dir = join_path(self%dep_dir, dep%name) call dep%git%checkout(proj_dir, error) if (allocated(error)) return @@ -454,6 +457,8 @@ subroutine resolve_dependency(self, dependency, root, error) character(len=:), allocatable :: manifest, proj_dir, revision logical :: fetch + print *, 'resolving dependency ',dependency%name,': done=',dependency%done,' update=',dependency%update + if (dependency%done) return fetch = .false. @@ -557,6 +562,7 @@ subroutine register(self, package, root, fetch, revision, error) type(error_t), allocatable, intent(out) :: error logical :: update + character(:), allocatable :: sver,pver update = .false. if (self%name /= package%name) then @@ -564,10 +570,26 @@ subroutine register(self, package, root, fetch, revision, error) & "' found, but expected '"//self%name//"' instead") end if + ! If this is the package node, always request an update of + ! the cache whenever its version changes + is_package: if (self%name==package%name .and. self%path==".") then + + if (self%version/=package%version) update = .true. + + end if is_package + + call self%version%to_string(sver) + call package%version%to_string(pver) + print *, 'self%version=',sver,' package version = ',pver + self%version = package%version + + print *, 'self%proj_dir=',self%proj_dir,' package dir = ',root + self%proj_dir = root if (allocated(self%git).and.present(revision)) then + print *, 'self revision = ',self%revision,' revision = ',revision,' fetch = ',fetch self%revision = revision if (.not.fetch) then ! git object is HEAD always allows an update @@ -582,6 +604,8 @@ subroutine register(self, package, root, fetch, revision, error) self%update = update self%done = .true. + print *, 'dep = ',self%name,' update=',update + end subroutine register !> Read dependency tree from file From 063148259fc4745f60c8e3d253c803b3e09ace38 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 20 Feb 2023 10:52:51 +0100 Subject: [PATCH 101/799] compare `cache.toml` and `fpm.toml` dependencies and force update when changed --- src/fpm.f90 | 1 - src/fpm/dependency.f90 | 79 ++++++++++++++++++++++----------- src/fpm/git.f90 | 17 +++++++ src/fpm/manifest/dependency.f90 | 27 +++++++++-- 4 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 19c9cbfbf0..ac22a70e47 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -63,7 +63,6 @@ subroutine build_model(model, settings, package, error) do i = 1, model%deps%ndep if (model%deps%dep(i)%update) then - print *, ' Updating model dependency ',model%deps%dep(i)%name,' ...' call model%deps%update(model%deps%dep(i)%name,error) if (allocated(error)) return end if diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 0b14d24669..9c7032c708 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -59,9 +59,10 @@ module fpm_dependency use fpm_environment, only : get_os_type, OS_WINDOWS use fpm_error, only : error_t, fatal_error use fpm_filesystem, only : exists, join_path, mkdir, canon_path, windows_path - use fpm_git, only : git_target_revision, git_target_default, git_revision + use fpm_git, only : git_target_revision, git_target_default, git_revision, operator(==) use fpm_manifest, only : package_config_t, dependency_config_t, & get_package_data + use fpm_manifest_dependency, only: manifest_has_changed use fpm_strings, only : string_t, operator(.in.) use fpm_toml, only : toml_table, toml_key, toml_error, toml_serializer, & toml_parse, get_value, set_value, add_table @@ -217,8 +218,6 @@ subroutine new_dependency_node(self, dependency, version, proj_dir, update) self%update = update end if - print *, 'new node from self=',self%name,' dep=',dependency%name, 'update=',update - end subroutine new_dependency_node !> Add project dependencies, each depth level after each other. @@ -368,12 +367,33 @@ subroutine add_dependency(self, dependency, error) type(error_t), allocatable, intent(out) :: error integer :: id + logical :: needs_update + type(dependency_node_t) :: new_dep id = self%find(dependency) - if (id == 0) then + + exists: if (id > 0) then + + !> A dependency with this same name is already in the dependency tree. + + !> check if it needs to be updated + call new_dependency_node(new_dep, dependency) + needs_update = dependency_has_changed(self%dep(id), new_dep) + + !> Ensure an update is requested whenever the dependency has changed + if (needs_update) then + write(self%unit, out_fmt) "Update needed:", dependency%name + call new_dependency_node(self%dep(id), dependency, update=.true.) + endif + + else exists + + !> New dependency: add from scratch self%ndep = self%ndep + 1 call new_dependency_node(self%dep(self%ndep), dependency) - end if + + end if exists + end subroutine add_dependency @@ -457,8 +477,6 @@ subroutine resolve_dependency(self, dependency, root, error) character(len=:), allocatable :: manifest, proj_dir, revision logical :: fetch - print *, 'resolving dependency ',dependency%name,': done=',dependency%done,' update=',dependency%update - if (dependency%done) return fetch = .false. @@ -562,7 +580,6 @@ subroutine register(self, package, root, fetch, revision, error) type(error_t), allocatable, intent(out) :: error logical :: update - character(:), allocatable :: sver,pver update = .false. if (self%name /= package%name) then @@ -570,26 +587,10 @@ subroutine register(self, package, root, fetch, revision, error) & "' found, but expected '"//self%name//"' instead") end if - ! If this is the package node, always request an update of - ! the cache whenever its version changes - is_package: if (self%name==package%name .and. self%path==".") then - - if (self%version/=package%version) update = .true. - - end if is_package - - call self%version%to_string(sver) - call package%version%to_string(pver) - print *, 'self%version=',sver,' package version = ',pver - self%version = package%version - - print *, 'self%proj_dir=',self%proj_dir,' package dir = ',root - self%proj_dir = root if (allocated(self%git).and.present(revision)) then - print *, 'self revision = ',self%revision,' revision = ',revision,' fetch = ',fetch self%revision = revision if (.not.fetch) then ! git object is HEAD always allows an update @@ -604,8 +605,6 @@ subroutine register(self, package, root, fetch, revision, error) self%update = update self%done = .true. - print *, 'dep = ',self%name,' update=',update - end subroutine register !> Read dependency tree from file @@ -835,4 +834,32 @@ pure subroutine resize_dependency_node(var, n) end subroutine resize_dependency_node + !> Check if a dependency node has changed + logical function dependency_has_changed(this,that) result(has_changed) + !> Two instances of the same dependency to be compared + type(dependency_node_t), intent(in) :: this,that + + has_changed = .true. + + !> All the following entities must be equal for the dependency to not have changed + if (manifest_has_changed(this, that)) return + + !> For now, only perform the following checks if both are available. A dependency in cache.toml + !> will always have this metadata; a dependency from fpm.toml which has not been fetched yet + !> may not have it + if (allocated(this%version) .and. allocated(that%version)) then + if (this%version/=that%version) return + endif + if (allocated(this%revision) .and. allocated(that%revision)) then + if (this%revision/=that%revision) return + endif + if (allocated(this%proj_dir) .and. allocated(that%proj_dir)) then + if (this%proj_dir/=that%proj_dir) return + endif + + !> All checks passed: the two dependencies have no differences + has_changed = .false. + + end function dependency_has_changed + end module fpm_dependency diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 46dcca3afa..8825fbaca8 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -8,6 +8,7 @@ module fpm_git public :: git_target_default, git_target_branch, git_target_tag, & & git_target_revision public :: git_revision + public :: operator(==) !> Possible git target @@ -54,6 +55,10 @@ module fpm_git end type git_target_t + interface operator(==) + module procedure git_target_eq + end interface + contains @@ -128,6 +133,18 @@ function git_target_tag(url, tag) result(self) end function git_target_tag + !> Check that two git targets are equal + logical function git_target_eq(this,that) result(is_equal) + + !> Two input git targets + type(git_target_t), intent(in) :: this,that + + is_equal = this%descriptor == that%descriptor .and. & + this%url == that%url .and. & + this%object == that%object + + end function git_target_eq + subroutine checkout(self, local_path, error) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 00f136472f..cf3c1a31d2 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -25,14 +25,14 @@ module fpm_manifest_dependency use fpm_error, only : error_t, syntax_error use fpm_git, only : git_target_t, git_target_tag, git_target_branch, & - & git_target_revision, git_target_default + & git_target_revision, git_target_default, operator(==) use fpm_toml, only : toml_table, toml_key, toml_stat, get_value use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS implicit none private - public :: dependency_config_t, new_dependency, new_dependencies + public :: dependency_config_t, new_dependency, new_dependencies, manifest_has_changed !> Configuration meta data for a dependency @@ -159,7 +159,7 @@ subroutine check(table, error) exit end if url_present = .true. - + case("path") if (url_present) then call syntax_error(error, "Dependency "//name//" cannot have both git and path entries") @@ -266,5 +266,26 @@ subroutine info(self, unit, verbosity) end subroutine info + !> Check if two dependency configurations are different + logical function manifest_has_changed(this, that) result(has_changed) + + !> Two instances of the dependency configuration + class(dependency_config_t), intent(in) :: this, that + + has_changed = .true. + + !> Perform all checks + if (this%name/=that%name) return + if (this%path/=that%path) return + if (allocated(this%git).neqv.allocated(that%git)) return + if (allocated(this%git)) then + if (.not.(this%git==that%git)) return + end if + + !> All checks passed! The two instances are equal + has_changed = .false. + + end function manifest_has_changed + end module fpm_manifest_dependency From d147ba3067c35e392585111398a22d71bc1692ad Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 20 Feb 2023 11:09:33 +0100 Subject: [PATCH 102/799] cleanup --- fpm.toml | 2 +- src/fpm.f90 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 25693520d6..9c694cab41 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.7.1" +version = "0.7.0" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/src/fpm.f90 b/src/fpm.f90 index ac22a70e47..34709777ff 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -60,6 +60,7 @@ subroutine build_model(model, settings, package, error) call model%deps%add(package, error) if (allocated(error)) return + ! Update dependencies where needed do i = 1, model%deps%ndep if (model%deps%dep(i)%update) then From 1793de2abb4f3bcfbdfa1a304888e3db9568651d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 22 Feb 2023 02:06:03 +0100 Subject: [PATCH 103/799] Parse package from remote registry --- fpm.toml | 4 + src/fpm.f90 | 2 +- src/fpm/dependency.f90 | 266 ++++++++++++++------ src/fpm_os.F90 | 2 +- src/fpm_settings.f90 | 36 +-- test/fpm_test/test_package_dependencies.f90 | 6 - 6 files changed, 207 insertions(+), 109 deletions(-) diff --git a/fpm.toml b/fpm.toml index 9c694cab41..1973f99c87 100644 --- a/fpm.toml +++ b/fpm.toml @@ -14,6 +14,10 @@ rev = "aee54c5a480d623af99828c76df0447a15ce90dc" git = "https://github.com/urbanjost/M_CLI2.git" rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" +[dependencies.json-fortran] +git = "https://github.com/jacobwilliams/json-fortran.git" +rev = "3ab8f98209871875325c6985dd0e50085d1c82c2" + [[test]] name = "cli-test" source-dir = "test/cli_test" diff --git a/src/fpm.f90 b/src/fpm.f90 index 6af602aedd..69db120331 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -478,7 +478,7 @@ subroutine cmd_run(settings,test) write(stderr,'(*(g0:,1x))') ' Execution failed for object "',basename(executables(i)%s),'"' end if end do - call fpm_stop(1,'*cmd_run*:stopping due to failed executions') + call fpm_stop(1,'*cmd_run*: Stopping due to failed executions') end if endif diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index adde924eef..ee28d645e9 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -58,7 +58,7 @@ module fpm_dependency use, intrinsic :: iso_fortran_env, only : output_unit use fpm_environment, only : get_os_type, OS_WINDOWS use fpm_error, only : error_t, fatal_error - use fpm_filesystem, only : exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename + use fpm_filesystem, only : exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, which use fpm_git, only : git_target_revision, git_target_default, git_revision use fpm_manifest, only : package_config_t, dependency_config_t, & get_package_data @@ -67,20 +67,17 @@ module fpm_dependency toml_parse, get_value, set_value, add_table, toml_load, toml_stat use fpm_versioning, only : version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings + use json_module implicit none private - public :: dependency_tree_t, new_dependency_tree - public :: dependency_node_t, new_dependency_node - public :: resize - + public :: dependency_tree_t, new_dependency_tree, dependency_node_t, new_dependency_node, resize !> Overloaded reallocation interface interface resize module procedure :: resize_dependency_node end interface resize - !> Dependency node in the projects dependency tree type, extends(dependency_config_t) :: dependency_node_t !> Actual version of this dependency @@ -94,11 +91,10 @@ module fpm_dependency !> Dependency should be updated logical :: update = .false. contains - !> Update dependency from project manifest - procedure :: register, get_from_registry, get_from_local_registry, get_from_remote_registry + procedure :: register, get_from_registry + procedure, private :: get_from_local_registry end type dependency_node_t - !> Respresentation of a projects dependencies !> !> The dependencies are stored in a simple array for now, this can be replaced @@ -501,48 +497,201 @@ end subroutine resolve_dependency !> by the global configuration settings. subroutine get_from_registry(self, target_dir, global_settings, error) - !> Instance of the dependency configuration. - class(dependency_node_t), intent(in) :: self + !> Instance of the dependency configuration. + class(dependency_node_t), intent(in) :: self - !> The target directory of the dependency. - character(:), allocatable, intent(out) :: target_dir + !> The target directory of the dependency. + character(:), allocatable, intent(out) :: target_dir - !> Global configuration settings. - type(fpm_global_settings), intent(inout) :: global_settings + !> Global configuration settings. + type(fpm_global_settings), intent(in) :: global_settings - !> Error handling. - type(error_t), allocatable, intent(out) :: error + !> Error handling. + type(error_t), allocatable, intent(out) :: error - ! Registry settings found in the global config file. - if (allocated(global_settings%registry_settings)) then - if (allocated(global_settings%registry_settings%path)) then - call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error) - return - end if - else - allocate (global_settings%registry_settings) + character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, versions, status_code, downloaded_version + type(string_t), allocatable :: files(:) + type(version_t) :: version + integer :: i, stat, unit + type(json_file) :: j_pkg + type(json_core) :: json + type(json_value), pointer :: j_obj, j_arr + logical :: is_found + character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' + + if (allocated(global_settings%registry_settings)) then + ! Use local registry if it was specified in the global config file. + if (allocated(global_settings%registry_settings%path)) then + call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error); return + end if + ! Use custom cache location if it was specified in the global config file. + if (allocated(global_settings%registry_settings%cache_path)) then + cache_path = global_settings%registry_settings%cache_path end if + end if + + ! Use default cache path if it wasn't specified in the global config file. + if (.not. allocated(cache_path)) then + cache_path = join_path(global_settings%path_to_config_folder, 'dependencies') + end if - if (.not. allocated(global_settings%registry_settings%cache_path)) then - ! Use default cache path if it wasn't set in the global config file. - global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, 'dependencies') + ! Include namespace and package name in the cache path. + cache_path = join_path(cache_path, self%namespace, self%name) + + ! Check cache before downloading from the remote registry if a specific version was requested. + if (allocated(self%requested_version)) then + if (exists(join_path(cache_path, self%requested_version%s(), 'fpm.toml'))) then + target_dir = cache_path; return end if + end if - if (.not. exists(global_settings%registry_settings%cache_path)) then - call mkdir(global_settings%registry_settings%cache_path) + ! Check if required programs are installed. + if (which('curl') == '') then + call fatal_error(error, "'curl' not installed."); return + else if (which('tar') == '') then + call fatal_error(error, "'tar' not installed."); return + end if + + ! Use custom registry url if it was specified in the global config file. + if (allocated(global_settings%registry_settings)) then + if (allocated(global_settings%registry_settings%url)) then + target_url = global_settings%registry_settings%url end if + end if - ! Check cache before downloading from remote registry when a specific version was requested. - if (allocated(self%requested_version)) then - if (exists(join_path(global_settings%registry_settings%cache_path, self%namespace, & - & self%name, self%requested_version%s()))) then - target_dir = join_path(global_settings%registry_settings%cache_path, self%namespace, & - & self%name, self%requested_version%s()) - return - end if + ! If no custom registry url was specified, use the official registry. + if (.not. allocated(target_url)) target_url = official_registry_base_url + + ! Include namespace and package name in the target url. + target_url = target_url//'/packages/'//self%namespace//'/'//self%name + + ! Define location of the temporary folder and file. + tmp_path = join_path(global_settings%path_to_config_folder, 'tmp') + if (.not. exists(tmp_path)) call mkdir(tmp_path) + tmp_file = join_path(tmp_path, 'package_data.tmp') + open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) + + if (stat /= 0) then + call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return + end if + + ! Make sure the cache path exists. + if (.not. exists(cache_path)) call mkdir(cache_path) + + ! Get package info from the registry and save it to a temporary file. + if (allocated (self%requested_version)) then + print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." + call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//'-s -o '//tmp_file, exitstat=stat) + else + ! Collect cached versions to send them to the registry for version resolution. + call json%create_object(j_obj, '') + call json%create_array(j_arr, 'cached_versions') + call json%add(j_obj, j_arr) + + call list_files(cache_path, files) + + if (size(files) > 0) then + do i = 1, size(files) + if (is_dir(files(i)%s) .and. exists(join_path(files(i)%s, 'fpm.toml'))) then + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + call json%add(j_arr, '', version%s()) + end if + end do end if - call self%get_from_remote_registry(target_dir, global_settings, error) + call json%serialize(j_obj, versions) + + print *, "Downloading '"//join_path(self%namespace, self%name)//"' ..." + call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) + + call json%destroy(j_obj) + end if + + if (stat /= 0) then + call fatal_error(error, "Error loading package '"//join_path(self%namespace, self%name)// & + & "' from the remote registry.") + close (unit, status='delete'); return + end if + + call j_pkg%initialize() + call j_pkg%load_file(tmp_file) + + close (unit, status='delete') + + if (j_pkg%failed()) then + call fatal_error(error, "Error reading package data of '"//join_path(self%namespace, self%name)//"'.") + call j_pkg%destroy(); return + end if + + call j_pkg%get('code', status_code, is_found) + + if (.not. is_found) then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No status code.") + call j_pkg%destroy(); return + end if + + if (status_code /= '200') then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': " & + & //"Status code '"//status_code//"'.") + call j_pkg%destroy(); return + end if + + ! Get download link and version of the package. + call j_pkg%get('tar', target_url, is_found) + + if (.not. is_found) then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No download link.") + call j_pkg%destroy(); return + end if + + ! Get version of the package. + call j_pkg%get('version', downloaded_version, is_found) + + if (.not. is_found) then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No version.") + call j_pkg%destroy(); return + end if + + call new_version(version, downloaded_version, error) + + if (allocated(error)) then + call fatal_error(error, "Version not valid: '"//downloaded_version//"'.") + call j_pkg%destroy(); return + end if + + call j_pkg%destroy() + + ! Open new temporary file for downloading the actual package. + open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) + + if (stat /= 0) then + call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return + end if + + call execute_command_line('curl '//target_url//' -o '//tmp_file, exitstat=stat) + + if (stat /= 0) then + call fatal_error(error, "Failed to download package '"//join_path(self%namespace, self%name)//"' from '"// & + & target_url//"'.") + close (unit, status='delete'); return + end if + + ! Include version number in the cache path. + cache_path = join_path(cache_path, version%s()) + if (.not. exists(cache_path)) call mkdir(cache_path) + + ! Unpack the downloaded package to the right location including its version number. + call execute_command_line('tar -zxf '//tmp_file//' -C '//cache_path, exitstat=stat) + + if (stat /= 0) then + call fatal_error(error, "Unpacking failed for '"//join_path(self%namespace, self%name)//"'.") + close (unit, status='delete'); return + end if + + close (unit, status='delete') + + target_dir = cache_path end subroutine get_from_registry @@ -617,7 +766,7 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) target_dir = join_path(path_to_name, version%s()) end subroutine get_from_local_registry - + !> Checks if the directory name matches the package version. subroutine check_version(dir_path, error) @@ -657,43 +806,6 @@ subroutine check_version(dir_path, error) end subroutine check_version - !> Get the dependency from a remote registry. - subroutine get_from_remote_registry(self, target_dir, global_settings, error) - - !> Instance of the dependency configuration. - class(dependency_node_t), intent(in) :: self - - !> The target directory to download the dependency to. - character(:), allocatable, intent(out) :: target_dir - - !> Global config settings. - type(fpm_global_settings), intent(in) :: global_settings - - !> Error handling. - type(error_t), allocatable, intent(out) :: error - - type(string_t), allocatable :: files(:) - type(version_t), allocatable :: versions(:) - type(version_t) :: version - integer :: i - - ! Collect existing versions from the cache. - call list_files(join_path(global_settings%registry_settings%cache_path, self%namespace, self%name), files) - - if (size(files) > 0) then - allocate (versions(0)) - do i = 1, size(files) - if (is_dir(files(i)%s)) then - call new_version(version, basename(files(i)%s), error) - if (allocated(error)) return - versions = [versions, version] - end if - end do - end if - ! Send version to registry and receive requested package. - ! Put it in the cache. - end subroutine get_from_remote_registry - !> True if dependency is part of the tree pure logical function has_dependency(self, dependency) !> Instance of the dependency tree diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index ec61cd732a..1acd9653ae 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -145,7 +145,7 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) -! Using gfortran, _WIN32 is currently not correctly exported on Windows +! The _WIN32 macro is currently not exported using gfortran. #if defined(FPM_BOOTSTRAP) && !defined(_WIN32) ptr = realpath(appended_path, cpath) #else diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index a1045f1f34..4ab3846383 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -55,14 +55,12 @@ subroutine get_global_settings(global_settings, error) if (global_settings%has_custom_location()) then ! Throw error if folder doesn't exist. if (.not. exists(global_settings%path_to_config_folder)) then - call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'.") - return + call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'."); return end if - ! Throw error if file doesn't exist. + ! Throw error if the file doesn't exist. if (.not. exists(global_settings%full_path())) then - call fatal_error(error, "File not found: '"//global_settings%full_path()//"'.") - return + call fatal_error(error, "File not found: '"//global_settings%full_path()//"'."); return end if ! Make sure that the path to the global config file is absolute. @@ -87,23 +85,19 @@ subroutine get_global_settings(global_settings, error) call toml_load(table, global_settings%full_path(), error=parse_error) if (allocated(parse_error)) then - allocate (error) - call move_alloc(parse_error%message, error%message) - return + allocate (error); call move_alloc(parse_error%message, error%message); return end if call get_value(table, 'registry', registry_table, requested=.false., stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Error reading registry from config file '"// & - & global_settings%full_path()//"'.") - return + & global_settings%full_path()//"'."); return end if ! A registry table was found. if (associated(registry_table)) then - call get_registry_settings(registry_table, global_settings, error) - return + call get_registry_settings(registry_table, global_settings, error); return end if end subroutine get_global_settings @@ -135,8 +129,7 @@ subroutine get_registry_settings(table, global_settings, error) call get_value(table, 'path', path, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry path: '"//path//"'.") - return + call fatal_error(error, "Error reading registry path: '"//path//"'."); return end if if (allocated(path)) then @@ -151,8 +144,7 @@ subroutine get_registry_settings(table, global_settings, error) ! Check if the path to the registry exists. if (.not. exists(global_settings%registry_settings%path)) then call fatal_error(error, "Directory '"//global_settings%registry_settings%path// & - & "' doesn't exist.") - return + & "' doesn't exist."); return end if end if end if @@ -160,15 +152,13 @@ subroutine get_registry_settings(table, global_settings, error) call get_value(table, 'url', url, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry url: '"//url//"'.") - return + call fatal_error(error, "Error reading registry url: '"//url//"'."); return end if if (allocated(url)) then ! Throw error when both path and url were provided. if (allocated(path)) then - call fatal_error(error, 'Do not provide both path and url to the registry.') - return + call fatal_error(error, 'Do not provide both path and url to the registry.'); return end if global_settings%registry_settings%url = url @@ -177,15 +167,13 @@ subroutine get_registry_settings(table, global_settings, error) call get_value(table, 'cache_path', cache_path, stat=stat) if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'.") - return + call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'."); return end if if (allocated(cache_path)) then ! Throw error when both path and cache_path were provided. if (allocated(path)) then - call fatal_error(error, "Do not provide both 'path' and 'cache_path'.") - return + call fatal_error(error, "Do not provide both 'path' and 'cache_path'."); return end if if (is_absolute_path(cache_path)) then diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 83b43d9262..2a3b8ff4ae 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -634,12 +634,6 @@ subroutine check_default_cache_path_no_dir(error) return end if - if (.not. exists(global_settings%registry_settings%cache_path)) then - call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") - call delete_tmp_folder - return - end if - call delete_tmp_folder end subroutine check_default_cache_path_no_dir From 102113d83ad3234144d469c175d62fbd9b3bd4dd Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 22 Feb 2023 18:52:05 +0100 Subject: [PATCH 104/799] Set default cache_path and url in settings --- src/fpm/dependency.f90 | 46 ++++++++++++------------------------------ src/fpm_settings.f90 | 22 ++++++++++++++------ 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index ee28d645e9..b323e74b50 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -517,30 +517,19 @@ subroutine get_from_registry(self, target_dir, global_settings, error) type(json_core) :: json type(json_value), pointer :: j_obj, j_arr logical :: is_found - character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' - if (allocated(global_settings%registry_settings)) then - ! Use local registry if it was specified in the global config file. - if (allocated(global_settings%registry_settings%path)) then - call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error); return - end if - ! Use custom cache location if it was specified in the global config file. - if (allocated(global_settings%registry_settings%cache_path)) then - cache_path = global_settings%registry_settings%cache_path - end if - end if - - ! Use default cache path if it wasn't specified in the global config file. - if (.not. allocated(cache_path)) then - cache_path = join_path(global_settings%path_to_config_folder, 'dependencies') + ! Use local registry if it was specified in the global config file. + if (allocated(global_settings%registry_settings%path)) then + call self%get_from_local_registry(target_dir, global_settings%registry_settings%path, error); return end if ! Include namespace and package name in the cache path. - cache_path = join_path(cache_path, self%namespace, self%name) + cache_path = join_path(global_settings%registry_settings%cache_path, self%namespace, self%name) ! Check cache before downloading from the remote registry if a specific version was requested. if (allocated(self%requested_version)) then if (exists(join_path(cache_path, self%requested_version%s(), 'fpm.toml'))) then + print *, "Using cached version of '", join_path(self%namespace, self%name, self%requested_version%s()), "'" target_dir = cache_path; return end if end if @@ -552,23 +541,13 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call fatal_error(error, "'tar' not installed."); return end if - ! Use custom registry url if it was specified in the global config file. - if (allocated(global_settings%registry_settings)) then - if (allocated(global_settings%registry_settings%url)) then - target_url = global_settings%registry_settings%url - end if - end if - - ! If no custom registry url was specified, use the official registry. - if (.not. allocated(target_url)) target_url = official_registry_base_url - ! Include namespace and package name in the target url. - target_url = target_url//'/packages/'//self%namespace//'/'//self%name - + target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name + ! Define location of the temporary folder and file. tmp_path = join_path(global_settings%path_to_config_folder, 'tmp') - if (.not. exists(tmp_path)) call mkdir(tmp_path) tmp_file = join_path(tmp_path, 'package_data.tmp') + if (.not. exists(tmp_path)) call mkdir(tmp_path) open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) if (stat /= 0) then @@ -580,8 +559,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error) ! Get package info from the registry and save it to a temporary file. if (allocated (self%requested_version)) then - print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." - call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//'-s -o '//tmp_file, exitstat=stat) + print *, "Downloading package data for '"//join_path(self%namespace, self%name, self%requested_version%s())//"' ..." + call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//' -s -o '//tmp_file, exitstat=stat) else ! Collect cached versions to send them to the registry for version resolution. call json%create_object(j_obj, '') @@ -602,7 +581,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call json%serialize(j_obj, versions) - print *, "Downloading '"//join_path(self%namespace, self%name)//"' ..." + print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) call json%destroy(j_obj) @@ -669,7 +648,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if - call execute_command_line('curl '//target_url//' -o '//tmp_file, exitstat=stat) + print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." + call execute_command_line('curl '//target_url//' -s -o '//tmp_file, exitstat=stat) if (stat /= 0) then call fatal_error(error, "Failed to download package '"//join_path(self%namespace, self%name)//"' from '"// & diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 4ab3846383..fb38cc50c2 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -10,6 +10,8 @@ module fpm_settings private public :: fpm_global_settings, get_global_settings, get_registry_settings + character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' + type :: fpm_global_settings !> Path to the global config file excluding the file name. character(len=:), allocatable :: path_to_config_folder @@ -98,6 +100,12 @@ subroutine get_global_settings(global_settings, error) ! A registry table was found. if (associated(registry_table)) then call get_registry_settings(registry_table, global_settings, error); return + else + ! No registry table was found, use default settings for url and cache_path. + allocate (global_settings%registry_settings) + global_settings%registry_settings%url = official_registry_base_url + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & + & 'dependencies'); return end if end subroutine get_global_settings @@ -160,8 +168,9 @@ subroutine get_registry_settings(table, global_settings, error) if (allocated(path)) then call fatal_error(error, 'Do not provide both path and url to the registry.'); return end if - global_settings%registry_settings%url = url + else if (.not. allocated(path)) then + global_settings%registry_settings%url = official_registry_base_url end if call get_value(table, 'cache_path', cache_path, stat=stat) @@ -180,14 +189,15 @@ subroutine get_registry_settings(table, global_settings, error) if (.not. exists(cache_path)) call mkdir(cache_path) global_settings%registry_settings%cache_path = cache_path else - if (.not. exists(join_path(global_settings%path_to_config_folder, cache_path))) then - call mkdir(join_path(global_settings%path_to_config_folder, cache_path)) - end if + cache_path = join_path(global_settings%path_to_config_folder, cache_path) + if (.not. exists(cache_path)) call mkdir(cache_path) ! Get canonical, absolute path on both Unix and Windows. - call get_absolute_path(join_path(global_settings%path_to_config_folder, cache_path), & - & global_settings%registry_settings%cache_path, error) + call get_absolute_path(cache_path, global_settings%registry_settings%cache_path, error) if (allocated(error)) return end if + else if (.not. allocated(path)) then + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & + & 'dependencies') end if end subroutine get_registry_settings From db3b2b8d3f4fa242cb594e5374e51e1a64336cf6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 22 Feb 2023 20:51:41 +0100 Subject: [PATCH 105/799] Fix tests in settings --- src/fpm_settings.f90 | 2 +- test/fpm_test/test_settings.f90 | 87 +++++++++++++++++---------------- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index fb38cc50c2..5e5e137236 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -8,7 +8,7 @@ module fpm_settings convert_to_absolute_path implicit none private - public :: fpm_global_settings, get_global_settings, get_registry_settings + public :: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index 87f734dd0c..c88bf6a5e9 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -1,6 +1,6 @@ module test_settings use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_settings, only: fpm_global_settings, get_global_settings, get_registry_settings + use fpm_settings, only: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists, get_local_prefix use fpm_environment, only: os_is_unix use fpm_toml, only: toml_table, new_table, add_table, set_value @@ -49,7 +49,7 @@ end subroutine collect_settings subroutine delete_tmp_folder if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - end + end subroutine subroutine setup_global_settings(global_settings, error) type(fpm_global_settings), intent(out) :: global_settings @@ -70,8 +70,10 @@ subroutine no_folder(error) type(fpm_global_settings) :: global_settings call delete_tmp_folder + call setup_global_settings(global_settings, error) if (allocated(error)) return + call get_global_settings(global_settings, error) end subroutine @@ -89,6 +91,35 @@ subroutine no_file(error) call get_global_settings(global_settings, error) end subroutine + !> No custom path and config file specified, use default path and file name. + subroutine default_config_settings(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + + character(:), allocatable :: default_path + + call delete_tmp_folder + + call get_global_settings(global_settings, error) + if (allocated(error)) return + + if (os_is_unix()) then + default_path = join_path(get_local_prefix(), 'share', 'fpm') + else + default_path = join_path(get_local_prefix(), 'fpm') + end if + + if (global_settings%path_to_config_folder /= default_path) then + call test_failed(error, "Path to config folder not set correctly :'"//global_settings%config_file_name//"'") + return + end if + + if (global_settings%config_file_name /= 'config.toml') then + call test_failed(error, "Config file name not set correctly :'"//global_settings%config_file_name//"'") + return + end if + end subroutine + !> Config file exists and the path to that file is set. subroutine empty_file(error) type(error_t), allocatable, intent(out) :: error @@ -115,42 +146,20 @@ subroutine empty_file(error) if (global_settings%path_to_config_folder /= join_path(cwd, tmp_folder)) then call test_failed(error, "global_settings%path_to_config_folder not set correctly :'" & - & //global_settings%path_to_config_folder//"'") - return - end if - - if (allocated(global_settings%registry_settings)) then - call test_failed(error, 'global_settings%registry_settings should not be allocated') - return + & //global_settings%path_to_config_folder//"'"); return end if - end subroutine - - !> No custom path and config file specified, use default path and file name. - subroutine default_config_settings(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(:), allocatable :: default_path - - call delete_tmp_folder - - call get_global_settings(global_settings, error) - if (allocated(error)) return - - if (os_is_unix()) then - default_path = join_path(get_local_prefix(), 'share', 'fpm') - else - default_path = join_path(get_local_prefix(), 'fpm') + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'global_settings%registry_settings not be allocated'); return end if - if (global_settings%path_to_config_folder /= default_path) then - call test_failed(error, "Path to config folder not set correctly :'"//global_settings%config_file_name//"'") - return + if (global_settings%registry_settings%url /= official_registry_base_url) then + call test_failed(error, 'Wrong default url'); return end if - if (global_settings%config_file_name /= 'config.toml') then - call test_failed(error, "Config file name not set correctly :'"//global_settings%config_file_name//"'") - return + if (global_settings%registry_settings%cache_path /= join_path(global_settings%path_to_config_folder, & + & 'dependencies')) then + call test_failed(error, 'Wrong default cache_path'); return end if end subroutine @@ -172,8 +181,7 @@ subroutine error_reading_table(error) call delete_tmp_folder if (allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings should not be allocated') - return + call test_failed(error, 'Registry settings should not be allocated'); return end if end subroutine @@ -190,18 +198,15 @@ subroutine empty_registry_table(error) if (allocated(error)) return if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated') - return + call test_failed(error, 'Registry settings not allocated'); return end if if (allocated(global_settings%registry_settings%path)) then - call test_failed(error, "Path shouldn't be allocated") - return + call test_failed(error, "Path shouldn't be allocated"); return end if - if (allocated(global_settings%registry_settings%url)) then - call test_failed(error, "Url shouldn't be allocated") - return + if (global_settings%registry_settings%url /= official_registry_base_url) then + call test_failed(error, "Url not be allocated"); return end if end subroutine From 51fd2fcb83aa399d0ea7437db0adc9c84befff93 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 23 Feb 2023 01:05:48 +0100 Subject: [PATCH 106/799] Fix tests in dependencies --- src/fpm/dependency.f90 | 15 +- test/fpm_test/main.f90 | 3 +- test/fpm_test/test_package_dependencies.f90 | 223 ++++++-------------- 3 files changed, 68 insertions(+), 173 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index b323e74b50..3af77870dd 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -528,7 +528,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error) ! Check cache before downloading from the remote registry if a specific version was requested. if (allocated(self%requested_version)) then - if (exists(join_path(cache_path, self%requested_version%s(), 'fpm.toml'))) then + cache_path = join_path(cache_path, self%requested_version%s()) + if (exists(join_path(cache_path, 'fpm.toml'))) then print *, "Using cached version of '", join_path(self%namespace, self%name, self%requested_version%s()), "'" target_dir = cache_path; return end if @@ -700,14 +701,12 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) if (.not. exists(path_to_name)) then call fatal_error(error, "Dependency resolution of '"//self%name// & - & "': Directory '"//path_to_name//"' doesn't exist.") - return + & "': Directory '"//path_to_name//"' doesn't exist."); return end if call list_files(path_to_name, files) if (size(files) == 0) then - call fatal_error(error, "No versions of '"//self%name//"' found in '"//path_to_name//"'.") - return + call fatal_error(error, "No versions of '"//self%name//"' found in '"//path_to_name//"'."); return end if ! Version requested, find it in the cache. @@ -715,8 +714,7 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) do i = 1, size(files) ! Identify directory that matches the version number. if (files(i)%s == join_path(path_to_name, self%requested_version%s()) .and. is_dir(files(i)%s)) then - target_dir = files(i)%s - return + target_dir = files(i)%s; return end if end do call fatal_error(error, "Version '"//self%requested_version%s()//"' not found in '"//path_to_name//"'") @@ -734,8 +732,7 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) end do if (size(versions) == 0) then - call fatal_error(error, "No versions found in '"//path_to_name//"'") - return + call fatal_error(error, "No versions found in '"//path_to_name//"'"); return end if ! Find the latest version. diff --git a/test/fpm_test/main.f90 b/test/fpm_test/main.f90 index eb063dccab..be97e4d70f 100644 --- a/test/fpm_test/main.f90 +++ b/test/fpm_test/main.f90 @@ -1,8 +1,7 @@ !> Driver for unit testing program fpm_testing use, intrinsic :: iso_fortran_env, only : error_unit - use testsuite, only : run_testsuite, new_testsuite, testsuite_t, & - & select_suite, run_selected + use testsuite, only : run_testsuite, new_testsuite, testsuite_t, select_suite, run_selected use test_toml, only : collect_toml use test_manifest, only : collect_manifest use test_filesystem, only : collect_filesystem diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 2a3b8ff4ae..9b4108a619 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -42,9 +42,8 @@ subroutine collect_package_dependencies(tests) & new_unittest("found-in-registry", version_found_in_registry), & & new_unittest("not-a-dir", not_a_dir, should_fail=.true.), & & new_unittest("newest-version-in-registry", newest_version_in_registry), & - & new_unittest("check-default-cache-path-has-dir", check_default_cache_path_has_dir), & - & new_unittest("check-default-cache-path-no-dir", check_default_cache_path_no_dir), & - & new_unittest("version-found-in-default-cache", version_found_in_default_cache), & + & new_unittest("default-cache-path", default_cache_path), & + & new_unittest("version-found-in-cache", version_found_in_cache), & & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache) & & ] @@ -243,28 +242,25 @@ subroutine registry_dir_not_found(error) if (allocated(error)) return call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache')) + call mkdir(join_path(tmp_folder, 'cache')) ! Missing directories for namesapce and package name call new_table(table) call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') ! Missing directories for namesapce and package name + call set_value(child, 'path', 'cache') call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -296,20 +292,17 @@ subroutine no_versions_in_registry(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -343,20 +336,17 @@ subroutine version_not_found_in_registry(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -391,32 +381,27 @@ subroutine version_found_in_registry(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_current_directory(cwd, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'))) then call test_failed(error, 'target_dir not set correctly') - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -449,20 +434,17 @@ subroutine not_a_dir(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -496,32 +478,27 @@ subroutine newest_version_in_registry(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_current_directory(cwd, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'))) then call test_failed(error, 'target_dir not set correctly: '//target_dir//"'") - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -529,7 +506,7 @@ subroutine newest_version_in_registry(error) end subroutine newest_version_in_registry !> No cache_path specified, use default cache path but folder exists already. - subroutine check_default_cache_path_has_dir(error) + subroutine default_cache_path(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -539,112 +516,55 @@ subroutine check_default_cache_path_has_dir(error) type(toml_table), pointer :: child call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') + table%key = 'version-f' + call set_value(table, 'namespace', 'minhdao') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'dependencies')) ! Dependency folder exists already + call mkdir(tmp_folder) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call new_table(table) - call add_table(table, 'registry', child) + call add_table(table, 'registry', child) ! No cache_path specified, use default call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if if (global_settings%registry_settings%cache_path /= & & join_path(global_settings%path_to_config_folder, 'dependencies')) then call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") - call delete_tmp_folder - return + call delete_tmp_folder; return end if if (.not. exists(global_settings%registry_settings%cache_path)) then call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder - end subroutine check_default_cache_path_has_dir + end subroutine default_cache_path - !> No cache_path specified, use default cache path and create folder. - subroutine check_default_cache_path_no_dir(error) + subroutine version_found_in_cache(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child - - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return - - call delete_tmp_folder - call mkdir(tmp_folder) - - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder - return - end if - - call new_table(table) - call add_table(table, 'registry', child) - - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder - return - end if - - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder - return - end if - - if (global_settings%registry_settings%cache_path /= & - & join_path(global_settings%path_to_config_folder, 'dependencies')) then - call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") - call delete_tmp_folder - return - end if - - call delete_tmp_folder - - end subroutine check_default_cache_path_no_dir - - subroutine version_found_in_default_cache(error) - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd + character(len=:), allocatable :: target_dir, cwd, path type(toml_table), pointer :: child call new_table(table) @@ -656,44 +576,41 @@ subroutine version_found_in_default_cache(error) if (allocated(error)) return call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0')) + path = join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0') + call mkdir(path) + call filewrite(join_path(path, 'fpm.toml'), ['']) call new_table(table) call add_table(table, 'registry', child) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_current_directory(cwd, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0'))) then - call test_failed(error, "Target directory not correctly set: '"//target_dir//"'") - call delete_tmp_folder - return + call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") + call delete_tmp_folder; return end if call delete_tmp_folder - end subroutine version_found_in_default_cache + end subroutine version_found_in_cache subroutine no_version_in_default_cache(error) type(error_t), allocatable, intent(out) :: error @@ -705,9 +622,9 @@ subroutine no_version_in_default_cache(error) type(toml_table), pointer :: child call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '2.3.0') + table%key = 'version-f' + call set_value(table, 'namespace', 'minhdao') + call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return @@ -720,26 +637,17 @@ subroutine no_version_in_default_cache(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return - end if - - if (allocated(target_dir)) then - call test_failed(error, 'Target directory should not be set') - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder @@ -756,9 +664,9 @@ subroutine other_versions_in_default_cache(error) type(toml_table), pointer :: child call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '2.3.0') + table%key = 'version-f' + call set_value(table, 'namespace', 'minhdao') + call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return @@ -772,26 +680,17 @@ subroutine other_versions_in_default_cache(error) call setup_global_settings(global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call get_registry_settings(child, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return + call delete_tmp_folder; return end if call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then - call delete_tmp_folder - return - end if - - if (allocated(target_dir)) then - call test_failed(error, 'Target directory should not be set') - call delete_tmp_folder - return + call delete_tmp_folder; return end if call delete_tmp_folder From ae3f93c71a85b0d4cdbc1db2abd834a87f71c9d7 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 24 Feb 2023 15:16:57 +0100 Subject: [PATCH 107/799] Do simple GET request if cache is empty --- src/fpm/dependency.f90 | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 3af77870dd..9dc4092af4 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -563,29 +563,36 @@ subroutine get_from_registry(self, target_dir, global_settings, error) print *, "Downloading package data for '"//join_path(self%namespace, self%name, self%requested_version%s())//"' ..." call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//' -s -o '//tmp_file, exitstat=stat) else - ! Collect cached versions to send them to the registry for version resolution. - call json%create_object(j_obj, '') - call json%create_array(j_arr, 'cached_versions') - call json%add(j_obj, j_arr) - call list_files(cache_path, files) + + if (size(files) == 0) then + ! No cached versions found, just download the latest version. + print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." + call execute_command_line('curl '//target_url//' -s -o '//tmp_file, exitstat=stat) + else + ! Cached versions found, send them to the registry for version resolution. + call json%create_object(j_obj, '') + call json%create_array(j_arr, 'cached_versions') + call json%add(j_obj, j_arr) - if (size(files) > 0) then do i = 1, size(files) - if (is_dir(files(i)%s) .and. exists(join_path(files(i)%s, 'fpm.toml'))) then + ! Verify these are fpm packages. + if (exists(join_path(files(i)%s, 'fpm.toml'))) then call new_version(version, basename(files(i)%s), error) if (allocated(error)) return call json%add(j_arr, '', version%s()) end if end do - end if - call json%serialize(j_obj, versions) + call json%serialize(j_obj, versions) + + print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." + call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) + + call json%destroy(j_obj) + end if - print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." - call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) - call json%destroy(j_obj) end if if (stat /= 0) then From a55d395e645647e23838a236f5082e6a2d89767e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 24 Feb 2023 15:17:28 +0100 Subject: [PATCH 108/799] Remove empty line --- src/fpm/dependency.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 9dc4092af4..024add88f8 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -592,7 +592,6 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call json%destroy(j_obj) end if - end if if (stat /= 0) then From 74e83bb22fa62354461ae3c529256e055a760fc9 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 25 Feb 2023 01:23:21 +0100 Subject: [PATCH 109/799] Use either curl or wget --- src/fpm/dependency.f90 | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 024add88f8..ee77c2c931 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -536,8 +536,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error) end if ! Check if required programs are installed. - if (which('curl') == '') then - call fatal_error(error, "'curl' not installed."); return + if (which('curl') == '' .and. which('wget') == '') then + call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return else if (which('tar') == '') then call fatal_error(error, "'tar' not installed."); return end if @@ -561,14 +561,22 @@ subroutine get_from_registry(self, target_dir, global_settings, error) ! Get package info from the registry and save it to a temporary file. if (allocated (self%requested_version)) then print *, "Downloading package data for '"//join_path(self%namespace, self%name, self%requested_version%s())//"' ..." - call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//' -s -o '//tmp_file, exitstat=stat) + if (which('curl') /= '') then + call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//' -s -o '//tmp_file, exitstat=stat) + else + call execute_command_line('wget '//target_url//'/'//self%requested_version%s()//' -q -O '//tmp_file, exitstat=stat) + end if else call list_files(cache_path, files) if (size(files) == 0) then ! No cached versions found, just download the latest version. print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." - call execute_command_line('curl '//target_url//' -s -o '//tmp_file, exitstat=stat) + if (which('curl') /= '') then + call execute_command_line('curl '//target_url//' -s -o '//tmp_file, exitstat=stat) + else + call execute_command_line('wget '//target_url//' -q -O '//tmp_file, exitstat=stat) + end if else ! Cached versions found, send them to the registry for version resolution. call json%create_object(j_obj, '') @@ -587,7 +595,11 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call json%serialize(j_obj, versions) print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." - call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) + if (which('curl') /= '') then + call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) + else + call execute_command_line('wget '//target_url//' -q -O '//tmp_file// ' --post-data="'//versions//'"', exitstat=stat) + end if call json%destroy(j_obj) end if From b2d2afc2ff6cd0078ae59573472b46a2ef1f9144 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 1 Mar 2023 14:50:15 +0100 Subject: [PATCH 110/799] Update whole dependency tree: make it a subroutine --- src/fpm.f90 | 10 ++-------- src/fpm/cmd/update.f90 | 6 ++---- src/fpm/dependency.f90 | 23 +++++++++++++++++++++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 34709777ff..6b1a338530 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -61,14 +61,8 @@ subroutine build_model(model, settings, package, error) if (allocated(error)) return ! Update dependencies where needed - do i = 1, model%deps%ndep - - if (model%deps%dep(i)%update) then - call model%deps%update(model%deps%dep(i)%name,error) - if (allocated(error)) return - end if - - end do + call model%deps%update(error) + if (allocated(error)) return ! build/ directory should now exist if (.not.exists("build/.gitignore")) then diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index a9918bf7ac..41e1dc3f14 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -43,10 +43,8 @@ subroutine cmd_update(settings) if (settings%fetch_only) return if (size(settings%name) == 0) then - do ii = 1, deps%ndep - call deps%update(deps%dep(ii)%name, error) - call handle_error(error) - end do + call deps%update(error) + call handle_error(error) else do ii = 1, size(settings%name) call deps%update(trim(settings%name(ii)), error) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 9c7032c708..9b57d03920 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -159,9 +159,11 @@ module fpm_dependency !> Write dependency tree to TOML data structure procedure, private :: dump_to_toml !> Update dependency tree - generic :: update => update_dependency + generic :: update => update_dependency,update_tree !> Update a list of dependencies procedure, private :: update_dependency + !> Update all dependencies in the tree + procedure, private :: update_tree end type dependency_tree_t !> Common output format for writing to the command line @@ -382,7 +384,7 @@ subroutine add_dependency(self, dependency, error) !> Ensure an update is requested whenever the dependency has changed if (needs_update) then - write(self%unit, out_fmt) "Update needed:", dependency%name + write(self%unit, out_fmt) "Dependency change detected:", dependency%name call new_dependency_node(self%dep(id), dependency, update=.true.) endif @@ -442,6 +444,23 @@ subroutine update_dependency(self, name, error) end subroutine update_dependency + !> Update whole dependency tree + subroutine update_tree(self, error) + !> Instance of the dependency tree + class(dependency_tree_t), intent(inout) :: self + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: i + + ! Update dependencies where needed + do i = 1, self%ndep + call self%update(self%dep(i)%name,error) + if (allocated(error)) return + end do + + end subroutine update_tree + !> Resolve all dependencies in the tree subroutine resolve_dependencies(self, root, error) !> Instance of the dependency tree From 7230efa56e89a7139e5ffe23d2da3020c0cdc422 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 1 Mar 2023 15:50:40 +0100 Subject: [PATCH 111/799] Extract command line executions into subroutine --- src/fpm/dependency.f90 | 111 +++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index ee77c2c931..3d6ced8195 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -535,10 +535,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error) end if end if - ! Check if required programs are installed. - if (which('curl') == '' .and. which('wget') == '') then - call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return - else if (which('tar') == '') then + ! Check if tar is installed. + if (which('tar') == '') then call fatal_error(error, "'tar' not installed."); return end if @@ -555,60 +553,41 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if - ! Make sure the cache path exists. - if (.not. exists(cache_path)) call mkdir(cache_path) - ! Get package info from the registry and save it to a temporary file. if (allocated (self%requested_version)) then - print *, "Downloading package data for '"//join_path(self%namespace, self%name, self%requested_version%s())//"' ..." - if (which('curl') /= '') then - call execute_command_line('curl '//target_url//'/'//self%requested_version%s()//' -s -o '//tmp_file, exitstat=stat) - else - call execute_command_line('wget '//target_url//'/'//self%requested_version%s()//' -q -O '//tmp_file, exitstat=stat) - end if + ! Request specific version. + call download_from_registry(target_url//'/'//self%requested_version%s(), tmp_file, error) else - call list_files(cache_path, files) - - if (size(files) == 0) then - ! No cached versions found, just download the latest version. - print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." - if (which('curl') /= '') then - call execute_command_line('curl '//target_url//' -s -o '//tmp_file, exitstat=stat) + if (exists(cache_path)) then + call list_files(cache_path, files) + if (size(files) == 0) then + ! Zero cached versions found, no need to send further data for version resolution. + call download_from_registry(target_url, tmp_file, error) else - call execute_command_line('wget '//target_url//' -q -O '//tmp_file, exitstat=stat) + ! Cached versions found, collect and send them to the registry for version resolution. + call json%create_object(j_obj, '') + call json%create_array(j_arr, 'cached_versions') + call json%add(j_obj, j_arr) + + do i = 1, size(files) + ! Verify these are fpm packages. + if (exists(join_path(files(i)%s, 'fpm.toml'))) then + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + call json%add(j_arr, '', version%s()) + end if + end do + + call json%serialize(j_obj, versions) + call download_from_registry(target_url, tmp_file, error, versions) + call json%destroy(j_obj) end if else - ! Cached versions found, send them to the registry for version resolution. - call json%create_object(j_obj, '') - call json%create_array(j_arr, 'cached_versions') - call json%add(j_obj, j_arr) - - do i = 1, size(files) - ! Verify these are fpm packages. - if (exists(join_path(files(i)%s, 'fpm.toml'))) then - call new_version(version, basename(files(i)%s), error) - if (allocated(error)) return - call json%add(j_arr, '', version%s()) - end if - end do - - call json%serialize(j_obj, versions) - - print *, "Downloading package data for '"//join_path(self%namespace, self%name)//"' ..." - if (which('curl') /= '') then - call execute_command_line('curl -X POST '//target_url//' -o '//tmp_file// ' -s -d "'//versions//'"', exitstat=stat) - else - call execute_command_line('wget '//target_url//' -q -O '//tmp_file// ' --post-data="'//versions//'"', exitstat=stat) - end if - - call json%destroy(j_obj) + call download_from_registry(target_url, tmp_file, error) end if - end if - if (stat /= 0) then - call fatal_error(error, "Error loading package '"//join_path(self%namespace, self%name)// & - & "' from the remote registry.") + if (allocated(error)) then close (unit, status='delete'); return end if @@ -762,6 +741,40 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) target_dir = join_path(path_to_name, version%s()) end subroutine get_from_local_registry + subroutine download_from_registry(url, tmp_file, error, post_data) + character(*), intent(in) :: url + character(*), intent(in) :: tmp_file + type(error_t), allocatable, intent(out) :: error + character(*), optional, intent(in) :: post_data + + character(:), allocatable :: extra_args + integer :: stat + + if (which('curl') /= '') then + if (present(post_data)) then + extra_args = ' -X POST -d "'//post_data//'"' + else + extra_args = '' + end if + print *, "Downloading package data from '"//url//"' ..." + call execute_command_line('curl '//url//' -s -o '//tmp_file//extra_args, exitstat=stat) + else if (which('wget') /= '') then + if (present(post_data)) then + extra_args = ' --post-data="'//post_data//'"' + else + extra_args = '' + end if + print *, "Downloading package data from '"//url//"' ..." + call execute_command_line('wget '//url//' -q -O '//tmp_file//extra_args, exitstat=stat) + else + call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return + end if + + if (stat /= 0) then + call fatal_error(error, "Error downloading package from '"//url//"'."); return + end if + end subroutine download_from_registry + !> Checks if the directory name matches the package version. subroutine check_version(dir_path, error) From 47ce48db5071128c59c66a5179607dcedc70c078 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 1 Mar 2023 16:01:02 +0100 Subject: [PATCH 112/799] dependency tree: `add` from `dependency_node_t` --- src/fpm/dependency.f90 | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 9b57d03920..f48286c768 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -119,7 +119,7 @@ module fpm_dependency contains !> Overload procedure to add new dependencies to the tree generic :: add => add_project, add_project_dependencies, add_dependencies, & - add_dependency + add_dependency, add_dependency_node !> Main entry point to add a project procedure, private :: add_project !> Add a project and its dependencies to the dependency tree @@ -128,6 +128,8 @@ module fpm_dependency procedure, private :: add_dependencies !> Add a single dependency to the dependency tree procedure, private :: add_dependency + !> Add a single dependency node to the dependency tree + procedure, private :: add_dependency_node !> Resolve dependencies generic :: resolve => resolve_dependencies, resolve_dependency !> Resolve dependencies @@ -359,18 +361,18 @@ subroutine add_dependencies(self, dependency, error) end subroutine add_dependencies - !> Add a single dependency to the dependency tree - subroutine add_dependency(self, dependency, error) + !> Add a single dependency node to the dependency tree + !> Dependency nodes contain additional information (version, git, revision) + subroutine add_dependency_node(self, dependency, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> Dependency configuration to add - type(dependency_config_t), intent(in) :: dependency + type(dependency_node_t), intent(in) :: dependency !> Error handling type(error_t), allocatable, intent(out) :: error integer :: id logical :: needs_update - type(dependency_node_t) :: new_dep id = self%find(dependency) @@ -379,23 +381,38 @@ subroutine add_dependency(self, dependency, error) !> A dependency with this same name is already in the dependency tree. !> check if it needs to be updated - call new_dependency_node(new_dep, dependency) - needs_update = dependency_has_changed(self%dep(id), new_dep) + needs_update = dependency_has_changed(self%dep(id), dependency) !> Ensure an update is requested whenever the dependency has changed if (needs_update) then write(self%unit, out_fmt) "Dependency change detected:", dependency%name - call new_dependency_node(self%dep(id), dependency, update=.true.) + self%dep(id) = dependency + self%dep(id)%update = .true. endif else exists !> New dependency: add from scratch self%ndep = self%ndep + 1 - call new_dependency_node(self%dep(self%ndep), dependency) + self%dep(self%ndep) = dependency end if exists + end subroutine add_dependency_node + + !> Add a single dependency to the dependency tree + subroutine add_dependency(self, dependency, error) + !> Instance of the dependency tree + class(dependency_tree_t), intent(inout) :: self + !> Dependency configuration to add + type(dependency_config_t), intent(in) :: dependency + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + + call new_dependency_node(node, dependency) + call add_dependency_node(self, node, error) end subroutine add_dependency From 9d45cd109ec01a05ff7fe4648583f6e497b45af0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 1 Mar 2023 16:02:12 +0100 Subject: [PATCH 113/799] override `info` output for dependency nodes --- src/fpm/dependency.f90 | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index f48286c768..ef2993563b 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -96,6 +96,8 @@ module fpm_dependency contains !> Update dependency from project manifest procedure :: register + !> Print information on this instance + procedure :: info end type dependency_node_t @@ -224,6 +226,49 @@ subroutine new_dependency_node(self, dependency, version, proj_dir, update) end subroutine new_dependency_node + !> Write information on instance + subroutine info(self, unit, verbosity) + + !> Instance of the dependency configuration + class(dependency_node_t), intent(in) :: self + + !> Unit for IO + integer, intent(in) :: unit + + !> Verbosity of the printout + integer, intent(in), optional :: verbosity + + integer :: pr + character(:), allocatable :: ver + character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)' + + if (present(verbosity)) then + pr = verbosity + else + pr = 1 + end if + + !> Call base object info + call self%dependency_config_t%info(unit,pr) + + if (allocated(self%version)) then + call self%version%to_string(ver) + write(unit, fmt) "- version", ver + end if + + if (allocated(self%proj_dir)) then + write(unit, fmt) "- dir", self%proj_dir + end if + + if (allocated(self%revision)) then + write(unit, fmt) "- revision", self%revision + end if + + write(unit, fmt) "- done", merge('YES','NO ',self%done) + write(unit, fmt) "- update", merge('YES','NO ',self%update) + + end subroutine info + !> Add project dependencies, each depth level after each other. !> !> We implement this algorithm in an interative rather than a recursive fashion From 7d4f190e134b3ca97458ccba3674c071411ef5da Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 1 Mar 2023 16:03:01 +0100 Subject: [PATCH 114/799] dependency update: add unit test --- test/fpm_test/test_package_dependencies.f90 | 80 ++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 26f6852a0e..476d478a45 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -30,7 +30,8 @@ subroutine collect_package_dependencies(testsuite) & new_unittest("cache-load-dump", test_cache_load_dump), & & new_unittest("cache-dump-load", test_cache_dump_load), & & new_unittest("status-after-load", test_status), & - & new_unittest("add-dependencies", test_add_dependencies)] + & new_unittest("add-dependencies", test_add_dependencies), & + & new_unittest("update-dependencies",test_update_dependencies)] end subroutine collect_package_dependencies @@ -213,6 +214,83 @@ subroutine test_add_dependencies(error) end subroutine test_add_dependencies + subroutine test_update_dependencies(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: cache,manifest + type(toml_table), pointer :: ptr + type(toml_key), allocatable :: list(:) + type(dependency_tree_t) :: deps,cached_deps + integer :: ii + + ! Create a dummy cache + cache = toml_table() + call add_table(cache, "dep1", ptr) + call set_value(ptr, "version", "1.1.0") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(cache, "dep2", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/lin2") + call set_value(ptr, "rev", "c0ffee") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(cache, "dep3", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/pkg3") + call set_value(ptr, "rev", "t4a") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(cache, "dep4", ptr) + call set_value(ptr, "version", "1.0.0") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + + ! Load into a dependency tree + call new_dependency_tree(cached_deps) + call cached_deps%load(cache, error) + call cache%destroy() + if (allocated(error)) return + + ! Create a dummy manifest, with different version + manifest = toml_table() + call add_table(manifest, "dep1", ptr) + call set_value(ptr, "version", "1.1.1") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(manifest, "dep2", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/lin4") + call set_value(ptr, "rev", "c0ffee") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(manifest, "dep3", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/pkg3") + call set_value(ptr, "rev", "l4tte") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + + ! Load dependencies from manifest + call new_dependency_tree(deps) + call deps%load(manifest, error) + call manifest%destroy() + if (allocated(error)) return + + ! Add manifest dependencies + do ii=1,cached_deps%ndep + call deps%add(cached_deps%dep(ii),error) + if (allocated(error)) return + end do + + ! Test that all dependencies are flagged as "update" + if (.not.deps%dep(1)%update) then + call test_failed(error, "Updated dependency (different version) not detected") + return + end if + if (.not.deps%dep(2)%update) then + call test_failed(error, "Updated dependency (git address) not detected") + return + end if + if (.not.deps%dep(3)%update) then + call test_failed(error, "Updated dependency (git rev) not detected") + return + end if + + + end subroutine test_update_dependencies + !> Resolve a single dependency node subroutine resolve_dependency_once(self, dependency, root, error) From 2af576c25cede932000611bd26181b973358025c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 1 Mar 2023 16:12:15 +0100 Subject: [PATCH 115/799] Invert condition for higher readability --- src/fpm/dependency.f90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 3d6ced8195..829283a8f9 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -558,7 +558,10 @@ subroutine get_from_registry(self, target_dir, global_settings, error) ! Request specific version. call download_from_registry(target_url//'/'//self%requested_version%s(), tmp_file, error) else - if (exists(cache_path)) then + if (.not. exists(cache_path)) then + ! No cached versions found, no need to send further data for version resolution. + call download_from_registry(target_url, tmp_file, error) + else call list_files(cache_path, files) if (size(files) == 0) then ! Zero cached versions found, no need to send further data for version resolution. @@ -582,8 +585,6 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call download_from_registry(target_url, tmp_file, error, versions) call json%destroy(j_obj) end if - else - call download_from_registry(target_url, tmp_file, error) end if end if From 763e6b931502d47de253272fb4e14b0d393966f4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 1 Mar 2023 23:58:55 +0100 Subject: [PATCH 116/799] Extract downloading and unpacking to fpm_downloader for testing purposes, remove check_version, accidentally format file --- src/fpm/dependency.f90 | 308 ++++++++++++++--------------------------- 1 file changed, 103 insertions(+), 205 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 829283a8f9..da49985788 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -55,18 +55,19 @@ !> !> Currenly ignored. First come, first serve. module fpm_dependency - use, intrinsic :: iso_fortran_env, only : output_unit - use fpm_environment, only : get_os_type, OS_WINDOWS - use fpm_error, only : error_t, fatal_error - use fpm_filesystem, only : exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, which - use fpm_git, only : git_target_revision, git_target_default, git_revision - use fpm_manifest, only : package_config_t, dependency_config_t, & - get_package_data - use fpm_strings, only : string_t, operator(.in.) - use fpm_toml, only : toml_table, toml_key, toml_error, toml_serializer, & - toml_parse, get_value, set_value, add_table, toml_load, toml_stat - use fpm_versioning, only : version_t, new_version + use, intrinsic :: iso_fortran_env, only: output_unit + use fpm_environment, only: get_os_type, OS_WINDOWS + use fpm_error, only: error_t, fatal_error + use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, which + use fpm_git, only: git_target_revision, git_target_default, git_revision + use fpm_manifest, only: package_config_t, dependency_config_t, & + get_package_data + use fpm_strings, only: string_t, operator(.in.) + use fpm_toml, only: toml_table, toml_key, toml_error, toml_serializer, & + toml_parse, get_value, set_value, add_table, toml_load, toml_stat + use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings + use fpm_downloader, only: downloader_t use json_module implicit none private @@ -238,7 +239,7 @@ subroutine add_project(self, package, error) if (allocated(error)) return end if - if (.not.exists(self%dep_dir)) then + if (.not. exists(self%dep_dir)) then call mkdir(self%dep_dir) end if @@ -257,9 +258,9 @@ subroutine add_project(self, package, error) if (allocated(error)) return ! Now decent into the dependency tree, level for level - do while(.not.self%finished()) - call self%resolve(root, error) - if (allocated(error)) exit + do while (.not. self%finished()) + call self%resolve(root, error) + if (allocated(error)) exit end do if (allocated(error)) return @@ -389,10 +390,10 @@ subroutine update_dependency(self, name, error) return end if - associate(dep => self%dep(id)) + associate (dep => self%dep(id)) if (allocated(dep%git) .and. dep%update) then if (self%verbosity > 1) then - write(self%unit, out_fmt) "Update:", dep%name + write (self%unit, out_fmt) "Update:", dep%name end if proj_dir = join_path(self%dep_dir, dep%name) call dep%git%checkout(proj_dir, error) @@ -403,7 +404,7 @@ subroutine update_dependency(self, name, error) dep%update = .false. ! Now decent into the dependency tree, level for level - do while(.not.self%finished()) + do while (.not. self%finished()) call self%resolve(root, error) if (allocated(error)) exit end do @@ -482,7 +483,7 @@ subroutine resolve_dependency(self, dependency, root, error) if (allocated(error)) return if (self%verbosity > 1) then - write(self%unit, out_fmt) & + write (self%unit, out_fmt) & "Dep:", dependency%name, "version", dependency%version%s(), & "at", dependency%proj_dir end if @@ -495,7 +496,7 @@ end subroutine resolve_dependency !> Get a dependency from the registry. Whether the dependency is fetched !> from a local, a custom remote or the official registry is determined !> by the global configuration settings. - subroutine get_from_registry(self, target_dir, global_settings, error) + subroutine get_from_registry(self, target_dir, global_settings, error, downloader) !> Instance of the dependency configuration. class(dependency_node_t), intent(in) :: self @@ -505,17 +506,17 @@ subroutine get_from_registry(self, target_dir, global_settings, error) !> Global configuration settings. type(fpm_global_settings), intent(in) :: global_settings - + !> Error handling. type(error_t), allocatable, intent(out) :: error - character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, versions, status_code, downloaded_version - type(string_t), allocatable :: files(:) + !> Downloader instance. + type(downloader_t), optional, intent(in) :: downloader + + character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, status_code, downloaded_version type(version_t) :: version - integer :: i, stat, unit + integer :: stat, unit type(json_file) :: j_pkg - type(json_core) :: json - type(json_value), pointer :: j_obj, j_arr logical :: is_found ! Use local registry if it was specified in the global config file. @@ -553,39 +554,12 @@ subroutine get_from_registry(self, target_dir, global_settings, error) call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if - ! Get package info from the registry and save it to a temporary file. - if (allocated (self%requested_version)) then + if (allocated(self%requested_version)) then ! Request specific version. - call download_from_registry(target_url//'/'//self%requested_version%s(), tmp_file, error) + call downloader%get(target_url//'/'//self%requested_version%s(), tmp_file, error) else - if (.not. exists(cache_path)) then - ! No cached versions found, no need to send further data for version resolution. - call download_from_registry(target_url, tmp_file, error) - else - call list_files(cache_path, files) - if (size(files) == 0) then - ! Zero cached versions found, no need to send further data for version resolution. - call download_from_registry(target_url, tmp_file, error) - else - ! Cached versions found, collect and send them to the registry for version resolution. - call json%create_object(j_obj, '') - call json%create_array(j_arr, 'cached_versions') - call json%add(j_obj, j_arr) - - do i = 1, size(files) - ! Verify these are fpm packages. - if (exists(join_path(files(i)%s, 'fpm.toml'))) then - call new_version(version, basename(files(i)%s), error) - if (allocated(error)) return - call json%add(j_arr, '', version%s()) - end if - end do - - call json%serialize(j_obj, versions) - call download_from_registry(target_url, tmp_file, error, versions) - call json%destroy(j_obj) - end if - end if + ! Request latest version. + call downloader%get(target_url, tmp_file, error) end if if (allocated(error)) then @@ -648,7 +622,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error) end if print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." - call execute_command_line('curl '//target_url//' -s -o '//tmp_file, exitstat=stat) + call downloader%get(target_url, tmp_file, error) if (stat /= 0) then call fatal_error(error, "Failed to download package '"//join_path(self%namespace, self%name)//"' from '"// & @@ -659,17 +633,14 @@ subroutine get_from_registry(self, target_dir, global_settings, error) ! Include version number in the cache path. cache_path = join_path(cache_path, version%s()) if (.not. exists(cache_path)) call mkdir(cache_path) - + ! Unpack the downloaded package to the right location including its version number. - call execute_command_line('tar -zxf '//tmp_file//' -C '//cache_path, exitstat=stat) - - if (stat /= 0) then - call fatal_error(error, "Unpacking failed for '"//join_path(self%namespace, self%name)//"'.") - close (unit, status='delete'); return - end if - + call downloader%unpack(tmp_file, cache_path, error) + close (unit, status='delete') + if (allocated(error)) return + target_dir = cache_path end subroutine get_from_registry @@ -677,143 +648,70 @@ end subroutine get_from_registry !> Get the dependency from a local registry. subroutine get_from_local_registry(self, target_dir, registry_path, error) - !> Instance of the dependency configuration. - class(dependency_node_t), intent(in) :: self - - !> The target directory to download the dependency to. - character(:), allocatable, intent(out) :: target_dir + !> Instance of the dependency configuration. + class(dependency_node_t), intent(in) :: self - !> The path to the local registry. - character(*), intent(in) :: registry_path + !> The target directory to download the dependency to. + character(:), allocatable, intent(out) :: target_dir - !> Error handling. - type(error_t), allocatable, intent(out) :: error + !> The path to the local registry. + character(*), intent(in) :: registry_path - character(:), allocatable :: path_to_name - type(string_t), allocatable :: files(:) - type(version_t), allocatable :: versions(:) - type(version_t) :: version - integer :: i + !> Error handling. + type(error_t), allocatable, intent(out) :: error - path_to_name = join_path(registry_path, self%namespace, self%name) + character(:), allocatable :: path_to_name + type(string_t), allocatable :: files(:) + type(version_t), allocatable :: versions(:) + type(version_t) :: version + integer :: i - if (.not. exists(path_to_name)) then - call fatal_error(error, "Dependency resolution of '"//self%name// & - & "': Directory '"//path_to_name//"' doesn't exist."); return - end if + path_to_name = join_path(registry_path, self%namespace, self%name) - call list_files(path_to_name, files) - if (size(files) == 0) then - call fatal_error(error, "No versions of '"//self%name//"' found in '"//path_to_name//"'."); return - end if + if (.not. exists(path_to_name)) then + call fatal_error(error, "Dependency resolution of '"//self%name// & + & "': Directory '"//path_to_name//"' doesn't exist."); return + end if - ! Version requested, find it in the cache. - if (allocated(self%requested_version)) then - do i = 1, size(files) - ! Identify directory that matches the version number. - if (files(i)%s == join_path(path_to_name, self%requested_version%s()) .and. is_dir(files(i)%s)) then - target_dir = files(i)%s; return - end if - end do - call fatal_error(error, "Version '"//self%requested_version%s()//"' not found in '"//path_to_name//"'") - return - end if + call list_files(path_to_name, files) + if (size(files) == 0) then + call fatal_error(error, "No versions of '"//self%name//"' found in '"//path_to_name//"'."); return + end if - ! No version requested, generate list of available versions. - allocate (versions(0)) + ! Version requested, find it in the cache. + if (allocated(self%requested_version)) then do i = 1, size(files) - if (is_dir(files(i)%s)) then - call new_version(version, basename(files(i)%s), error) - if (allocated(error)) return - versions = [versions, version] + ! Identify directory that matches the version number. + if (files(i)%s == join_path(path_to_name, self%requested_version%s()) .and. is_dir(files(i)%s)) then + target_dir = files(i)%s; return end if end do + call fatal_error(error, "Version '"//self%requested_version%s()//"' not found in '"//path_to_name//"'") + return + end if - if (size(versions) == 0) then - call fatal_error(error, "No versions found in '"//path_to_name//"'"); return - end if - - ! Find the latest version. - version = versions(1) - do i = 1, size(versions) - if (versions(i) > version) version = versions(i) - end do - - target_dir = join_path(path_to_name, version%s()) - end subroutine get_from_local_registry - - subroutine download_from_registry(url, tmp_file, error, post_data) - character(*), intent(in) :: url - character(*), intent(in) :: tmp_file - type(error_t), allocatable, intent(out) :: error - character(*), optional, intent(in) :: post_data - - character(:), allocatable :: extra_args - integer :: stat - - if (which('curl') /= '') then - if (present(post_data)) then - extra_args = ' -X POST -d "'//post_data//'"' - else - extra_args = '' - end if - print *, "Downloading package data from '"//url//"' ..." - call execute_command_line('curl '//url//' -s -o '//tmp_file//extra_args, exitstat=stat) - else if (which('wget') /= '') then - if (present(post_data)) then - extra_args = ' --post-data="'//post_data//'"' - else - extra_args = '' - end if - print *, "Downloading package data from '"//url//"' ..." - call execute_command_line('wget '//url//' -q -O '//tmp_file//extra_args, exitstat=stat) - else - call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return - end if - - if (stat /= 0) then - call fatal_error(error, "Error downloading package from '"//url//"'."); return - end if - end subroutine download_from_registry - - !> Checks if the directory name matches the package version. - subroutine check_version(dir_path, error) - - !> Absolute path to the package-containing directory. - character(*), intent(in) :: dir_path - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(toml_table), allocatable :: table - type(toml_error), allocatable :: parse_error - integer :: stat - character(:), allocatable :: version - - call toml_load(table, join_path(dir_path, 'fpm.toml'), error=parse_error) - - if (allocated(parse_error)) then - allocate (error) - call move_alloc(parse_error%message, error%message) - return + ! No version requested, generate list of available versions. + allocate (versions(0)) + do i = 1, size(files) + if (is_dir(files(i)%s)) then + call new_version(version, basename(files(i)%s), error) + if (allocated(error)) return + versions = [versions, version] end if + end do - call get_value(table, 'version', version, stat=stat) + if (size(versions) == 0) then + call fatal_error(error, "No versions found in '"//path_to_name//"'"); return + end if - if (stat /= toml_stat%success) then - call fatal_error(error, 'Error reading version number from "' & - //join_path(dir_path, 'fpm.toml')//'"') - return - end if + ! Find the latest version. + version = versions(1) + do i = 1, size(versions) + if (versions(i) > version) version = versions(i) + end do - if (version /= basename(dir_path)) then - call fatal_error(error, "Directory name '"//basename(dir_path) & - //"' does not match version number '"//version//" ' in package '"// & - dir_path//"'") - return - end if - - end subroutine check_version + target_dir = join_path(path_to_name, version%s()) + end subroutine get_from_local_registry !> True if dependency is part of the tree pure logical function has_dependency(self, dependency) @@ -884,12 +782,12 @@ subroutine register(self, package, root, fetch, revision, error) self%version = package%version self%proj_dir = root - if (allocated(self%git).and.present(revision)) then + if (allocated(self%git) .and. present(revision)) then self%revision = revision - if (.not.fetch) then + if (.not. fetch) then ! git object is HEAD always allows an update - update = .not.allocated(self%git%object) - if (.not.update) then + update = .not. allocated(self%git%object) + if (.not. update) then ! allow update in case the revision does not match the requested object update = revision /= self%git%object end if @@ -913,12 +811,12 @@ subroutine load_from_file(self, file, error) integer :: unit logical :: exist - inquire(file=file, exist=exist) - if (.not.exist) return + inquire (file=file, exist=exist) + if (.not. exist) return - open(file=file, newunit=unit) + open (file=file, newunit=unit) call self%load(unit, error) - close(unit) + close (unit) end subroutine load_from_file !> Read dependency tree from file @@ -936,7 +834,7 @@ subroutine load_from_unit(self, unit, error) call toml_parse(table, unit, parse_error) if (allocated(parse_error)) then - allocate(error) + allocate (error) call move_alloc(parse_error%message, error%message) return end if @@ -977,9 +875,9 @@ subroutine load_from_toml(self, table, error) call get_value(ptr, "git", url) call get_value(ptr, "obj", obj) call get_value(ptr, "rev", rev) - if (.not.allocated(proj_dir)) cycle + if (.not. allocated(proj_dir)) cycle self%ndep = self%ndep + 1 - associate(dep => self%dep(self%ndep)) + associate (dep => self%dep(self%ndep)) dep%name = list(ii)%key if (unix) then dep%proj_dir = proj_dir @@ -988,7 +886,7 @@ subroutine load_from_toml(self, table, error) end if dep%done = .false. if (allocated(version)) then - if (.not.allocated(dep%version)) allocate(dep%version) + if (.not. allocated(dep%version)) allocate (dep%version) call new_version(dep%version, version, error) if (allocated(error)) exit end if @@ -1022,9 +920,9 @@ subroutine dump_to_file(self, file, error) integer :: unit - open(file=file, newunit=unit) + open (file=file, newunit=unit) call self%dump(unit, error) - close(unit) + close (unit) if (allocated(error)) return end subroutine dump_to_file @@ -1064,9 +962,9 @@ subroutine dump_to_toml(self, table, error) character(len=:), allocatable :: proj_dir do ii = 1, self%ndep - associate(dep => self%dep(ii)) + associate (dep => self%dep(ii)) call add_table(table, dep%name, ptr) - if (.not.associated(ptr)) then + if (.not. associated(ptr)) then call fatal_error(error, "Cannot create entry for "//dep%name) exit end if @@ -1114,12 +1012,12 @@ pure subroutine resize_dependency_node(var, n) new_size = this_size + this_size/2 + 1 end if - allocate(var(new_size)) + allocate (var(new_size)) if (allocated(tmp)) then this_size = min(size(tmp, 1), size(var, 1)) var(:this_size) = tmp(:this_size) - deallocate(tmp) + deallocate (tmp) end if end subroutine resize_dependency_node From 74c736bec9587f04221cb19db189cabb887595b2 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 2 Mar 2023 00:10:09 +0100 Subject: [PATCH 117/799] Commit missing file, fix error handling and check tar in unpack --- src/fpm/dependency.f90 | 13 +++------- src/fpm/downloader.f90 | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 src/fpm/downloader.f90 diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index da49985788..902a3cbb60 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -58,7 +58,7 @@ module fpm_dependency use, intrinsic :: iso_fortran_env, only: output_unit use fpm_environment, only: get_os_type, OS_WINDOWS use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, which + use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename use fpm_git, only: git_target_revision, git_target_default, git_revision use fpm_manifest, only: package_config_t, dependency_config_t, & get_package_data @@ -536,11 +536,6 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end if end if - ! Check if tar is installed. - if (which('tar') == '') then - call fatal_error(error, "'tar' not installed."); return - end if - ! Include namespace and package name in the target url. target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name @@ -624,9 +619,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." call downloader%get(target_url, tmp_file, error) - if (stat /= 0) then - call fatal_error(error, "Failed to download package '"//join_path(self%namespace, self%name)//"' from '"// & - & target_url//"'.") + if (allocated(error)) then close (unit, status='delete'); return end if @@ -634,7 +627,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade cache_path = join_path(cache_path, version%s()) if (.not. exists(cache_path)) call mkdir(cache_path) - ! Unpack the downloaded package to the right location including its version number. + ! Unpack the downloaded package to the final location. call downloader%unpack(tmp_file, cache_path, error) close (unit, status='delete') diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 new file mode 100644 index 0000000000..1ccf342098 --- /dev/null +++ b/src/fpm/downloader.f90 @@ -0,0 +1,58 @@ +module fpm_downloader + use fpm_error, only: error_t, fatal_error + use fpm_filesystem, only: which + + implicit none + private + + public :: downloader_t + + !> This type could be entirely avoided but it is quite practical because it can be mocked for testing. + type downloader_t + contains + procedure, nopass :: get, unpack + end type + +contains + + !> Perform an http get request and save output to file. + subroutine get(url, tmp_file, error) + character(*), intent(in) :: url + character(*), intent(in) :: tmp_file + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + if (which('curl') /= '') then + print *, "Downloading package data from '"//url//"' ..." + call execute_command_line('curl '//url//' -s -o '//tmp_file, exitstat=stat) + else if (which('wget') /= '') then + print *, "Downloading package data from '"//url//"' ..." + call execute_command_line('wget '//url//' -q -O '//tmp_file, exitstat=stat) + else + call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return + end if + + if (stat /= 0) then + call fatal_error(error, "Error downloading package from '"//url//"'."); return + end if + end + + subroutine unpack(tmp_file, destination, error) + character(*), intent(in) :: tmp_file + character(*), intent(in) :: destination + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + if (which('tar') == '') then + call fatal_error(error, "'tar' not installed."); return + end if + + call execute_command_line('tar -zxf '//tmp_file//' -C '//destination, exitstat=stat) + + if (stat /= 0) then + call fatal_error(error, "Error unpacking '"//tmp_file//"'."); return + end if + end +end From 4668f6bfadd45ca1f66c03d9d1a2e928ca3a0b63 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 2 Mar 2023 16:16:18 +0100 Subject: [PATCH 118/799] Format test_package_dependencies for easier development --- test/fpm_test/test_package_dependencies.f90 | 1284 +++++++++---------- 1 file changed, 642 insertions(+), 642 deletions(-) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 9b4108a619..e60cddf8f3 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -1,737 +1,737 @@ !> Define tests for the `fpm_dependency` module module test_package_dependencies - use fpm_filesystem, only: get_temp_filename - use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_filesystem, only: is_dir, join_path, filewrite, mkdir, os_delete_dir, exists - use fpm_environment, only: os_is_unix - use fpm_os, only: get_current_directory - use fpm_dependency - use fpm_manifest_dependency - use fpm_toml - use fpm_settings, only: fpm_global_settings, get_registry_settings + use fpm_filesystem, only: get_temp_filename + use testsuite, only: new_unittest, unittest_t, error_t, test_failed + use fpm_filesystem, only: is_dir, join_path, filewrite, mkdir, os_delete_dir, exists + use fpm_environment, only: os_is_unix + use fpm_os, only: get_current_directory + use fpm_dependency + use fpm_manifest_dependency + use fpm_toml + use fpm_settings, only: fpm_global_settings, get_registry_settings - implicit none - private + implicit none + private - public :: collect_package_dependencies + public :: collect_package_dependencies - character(*), parameter :: tmp_folder = 'tmp' - character(*), parameter :: config_file_name = 'config.toml' + character(*), parameter :: tmp_folder = 'tmp' + character(*), parameter :: config_file_name = 'config.toml' - type, extends(dependency_tree_t) :: mock_dependency_tree_t - contains - procedure :: resolve_dependency => resolve_dependency_once - end type mock_dependency_tree_t + type, extends(dependency_tree_t) :: mock_dependency_tree_t + contains + procedure :: resolve_dependency => resolve_dependency_once + end type mock_dependency_tree_t contains - !> Collect all exported unit tests - subroutine collect_package_dependencies(tests) - - !> Collection of tests - type(unittest_t), allocatable, intent(out) :: tests(:) - - tests = [ & - & new_unittest("cache-load-dump", test_cache_load_dump), & - & new_unittest("cache-dump-load", test_cache_dump_load), & - & new_unittest("status-after-load", test_status), & - & new_unittest("add-dependencies", test_add_dependencies), & - & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & - & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & - & new_unittest("version-not-found-in-registry", version_not_found_in_registry, should_fail=.true.), & - & new_unittest("found-in-registry", version_found_in_registry), & - & new_unittest("not-a-dir", not_a_dir, should_fail=.true.), & - & new_unittest("newest-version-in-registry", newest_version_in_registry), & - & new_unittest("default-cache-path", default_cache_path), & - & new_unittest("version-found-in-cache", version_found_in_cache), & - & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & - & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache) & - & ] - - end subroutine collect_package_dependencies - - !> Round trip of the dependency cache from a dependency tree to a TOML document - !> to a dependency tree - subroutine test_cache_dump_load(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(dependency_tree_t) :: deps - type(dependency_config_t) :: dep - integer :: unit - - call new_dependency_tree(deps) - call resize(deps%dep, 5) - deps%ndep = 3 - dep%name = "dep1" - dep%path = "fpm-tmp1-dir" - call new_dependency_node(deps%dep(1), dep, proj_dir=dep%path) - dep%name = "dep2" - dep%path = "fpm-tmp2-dir" - call new_dependency_node(deps%dep(2), dep, proj_dir=dep%path) - dep%name = "dep3" - dep%path = "fpm-tmp3-dir" - call new_dependency_node(deps%dep(3), dep, proj_dir=dep%path) - - open (newunit=unit, status='scratch') - call deps%dump(unit, error) - if (.not. allocated(error)) then - rewind (unit) - - call new_dependency_tree(deps) - call resize(deps%dep, 2) - call deps%load(unit, error) - close (unit) - end if - if (allocated(error)) return - - if (deps%ndep /= 3) then - call test_failed(error, "Expected three dependencies in loaded cache") - return - end if - - end subroutine test_cache_dump_load - - !> Round trip of the dependency cache from a TOML data structure to - !> a dependency tree to a TOML data structure - subroutine test_cache_load_dump(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(toml_table), pointer :: ptr - type(toml_key), allocatable :: list(:) - type(dependency_tree_t) :: deps - - table = toml_table() - call add_table(table, "dep1", ptr) - call set_value(ptr, "version", "1.1.0") - call set_value(ptr, "proj-dir", "fpm-tmp1-dir") - call add_table(table, "dep2", ptr) - call set_value(ptr, "version", "0.55.3") - call set_value(ptr, "proj-dir", "fpm-tmp2-dir") - call set_value(ptr, "git", "https://github.com/fortran-lang/dep2") - call add_table(table, "dep3", ptr) - call set_value(ptr, "version", "20.1.15") - call set_value(ptr, "proj-dir", "fpm-tmp3-dir") - call set_value(ptr, "git", "https://gitlab.com/fortran-lang/dep3") - call set_value(ptr, "rev", "c0ffee") - call add_table(table, "dep4", ptr) - call set_value(ptr, "proj-dir", "fpm-tmp4-dir") - - call new_dependency_tree(deps) - call deps%load(table, error) - if (allocated(error)) return - - if (deps%ndep /= 4) then - call test_failed(error, "Expected four dependencies in loaded cache") - return - end if - - call table%destroy - table = toml_table() - - call deps%dump(table, error) - if (allocated(error)) return - - call table%get_keys(list) - - if (size(list) /= 4) then - call test_failed(error, "Expected four dependencies in dumped cache") - return - end if - - end subroutine test_cache_load_dump - - subroutine test_status(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(toml_table), pointer :: ptr - type(dependency_tree_t) :: deps - - table = toml_table() - call add_table(table, "dep1", ptr) - call set_value(ptr, "version", "1.1.0") - call set_value(ptr, "proj-dir", "fpm-tmp1-dir") - call add_table(table, "dep2", ptr) - call set_value(ptr, "version", "0.55.3") - call set_value(ptr, "proj-dir", "fpm-tmp2-dir") - call set_value(ptr, "git", "https://github.com/fortran-lang/dep2") - - call new_dependency_tree(deps) - call deps%load(table, error) - if (allocated(error)) return - - if (deps%finished()) then - call test_failed(error, "Newly initialized dependency tree cannot be reolved") - return - end if - - end subroutine test_status - - subroutine test_add_dependencies(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(toml_table), pointer :: ptr - type(mock_dependency_tree_t) :: deps - type(dependency_config_t), allocatable :: nodes(:) - - table = toml_table() - call add_table(table, "sub1", ptr) - call set_value(ptr, "path", "external") - call add_table(table, "lin2", ptr) - call set_value(ptr, "git", "https://github.com/fortran-lang/lin2") - call add_table(table, "pkg3", ptr) - call set_value(ptr, "git", "https://gitlab.com/fortran-lang/pkg3") - call set_value(ptr, "rev", "c0ffee") - call add_table(table, "proj4", ptr) - call set_value(ptr, "path", "vendor") - - call new_dependencies(nodes, table, error=error) - if (allocated(error)) return - - call new_dependencies(nodes, table, root='.', error=error) - if (allocated(error)) return - - call new_dependency_tree(deps%dependency_tree_t) - call deps%add(nodes, error) - if (allocated(error)) return - - if (deps%finished()) then - call test_failed(error, "Newly added nodes cannot be already resolved") - return - end if - - if (deps%ndep /= 4) then - call test_failed(error, "Expected for dependencies in tree") - return - end if + !> Collect all exported unit tests + subroutine collect_package_dependencies(tests) + + !> Collection of tests + type(unittest_t), allocatable, intent(out) :: tests(:) + + tests = [ & + & new_unittest("cache-load-dump", test_cache_load_dump), & + & new_unittest("cache-dump-load", test_cache_dump_load), & + & new_unittest("status-after-load", test_status), & + & new_unittest("add-dependencies", test_add_dependencies), & + & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & + & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & + & new_unittest("version-not-found-in-registry", version_not_found_in_registry, should_fail=.true.), & + & new_unittest("found-in-registry", version_found_in_registry), & + & new_unittest("not-a-dir", not_a_dir, should_fail=.true.), & + & new_unittest("newest-version-in-registry", newest_version_in_registry), & + & new_unittest("default-cache-path", default_cache_path), & + & new_unittest("version-found-in-cache", version_found_in_cache), & + & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & + & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache) & + & ] + + end subroutine collect_package_dependencies + + !> Round trip of the dependency cache from a dependency tree to a TOML document + !> to a dependency tree + subroutine test_cache_dump_load(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(dependency_tree_t) :: deps + type(dependency_config_t) :: dep + integer :: unit + + call new_dependency_tree(deps) + call resize(deps%dep, 5) + deps%ndep = 3 + dep%name = "dep1" + dep%path = "fpm-tmp1-dir" + call new_dependency_node(deps%dep(1), dep, proj_dir=dep%path) + dep%name = "dep2" + dep%path = "fpm-tmp2-dir" + call new_dependency_node(deps%dep(2), dep, proj_dir=dep%path) + dep%name = "dep3" + dep%path = "fpm-tmp3-dir" + call new_dependency_node(deps%dep(3), dep, proj_dir=dep%path) + + open (newunit=unit, status='scratch') + call deps%dump(unit, error) + if (.not. allocated(error)) then + rewind (unit) + + call new_dependency_tree(deps) + call resize(deps%dep, 2) + call deps%load(unit, error) + close (unit) + end if + if (allocated(error)) return + + if (deps%ndep /= 3) then + call test_failed(error, "Expected three dependencies in loaded cache") + return + end if + + end subroutine test_cache_dump_load + + !> Round trip of the dependency cache from a TOML data structure to + !> a dependency tree to a TOML data structure + subroutine test_cache_load_dump(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(toml_table), pointer :: ptr + type(toml_key), allocatable :: list(:) + type(dependency_tree_t) :: deps + + table = toml_table() + call add_table(table, "dep1", ptr) + call set_value(ptr, "version", "1.1.0") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(table, "dep2", ptr) + call set_value(ptr, "version", "0.55.3") + call set_value(ptr, "proj-dir", "fpm-tmp2-dir") + call set_value(ptr, "git", "https://github.com/fortran-lang/dep2") + call add_table(table, "dep3", ptr) + call set_value(ptr, "version", "20.1.15") + call set_value(ptr, "proj-dir", "fpm-tmp3-dir") + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/dep3") + call set_value(ptr, "rev", "c0ffee") + call add_table(table, "dep4", ptr) + call set_value(ptr, "proj-dir", "fpm-tmp4-dir") + + call new_dependency_tree(deps) + call deps%load(table, error) + if (allocated(error)) return + + if (deps%ndep /= 4) then + call test_failed(error, "Expected four dependencies in loaded cache") + return + end if + + call table%destroy + table = toml_table() + + call deps%dump(table, error) + if (allocated(error)) return + + call table%get_keys(list) + + if (size(list) /= 4) then + call test_failed(error, "Expected four dependencies in dumped cache") + return + end if + + end subroutine test_cache_load_dump + + subroutine test_status(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(toml_table), pointer :: ptr + type(dependency_tree_t) :: deps + + table = toml_table() + call add_table(table, "dep1", ptr) + call set_value(ptr, "version", "1.1.0") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(table, "dep2", ptr) + call set_value(ptr, "version", "0.55.3") + call set_value(ptr, "proj-dir", "fpm-tmp2-dir") + call set_value(ptr, "git", "https://github.com/fortran-lang/dep2") + + call new_dependency_tree(deps) + call deps%load(table, error) + if (allocated(error)) return + + if (deps%finished()) then + call test_failed(error, "Newly initialized dependency tree cannot be reolved") + return + end if + + end subroutine test_status + + subroutine test_add_dependencies(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(toml_table), pointer :: ptr + type(mock_dependency_tree_t) :: deps + type(dependency_config_t), allocatable :: nodes(:) + + table = toml_table() + call add_table(table, "sub1", ptr) + call set_value(ptr, "path", "external") + call add_table(table, "lin2", ptr) + call set_value(ptr, "git", "https://github.com/fortran-lang/lin2") + call add_table(table, "pkg3", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/pkg3") + call set_value(ptr, "rev", "c0ffee") + call add_table(table, "proj4", ptr) + call set_value(ptr, "path", "vendor") + + call new_dependencies(nodes, table, error=error) + if (allocated(error)) return + + call new_dependencies(nodes, table, root='.', error=error) + if (allocated(error)) return + + call new_dependency_tree(deps%dependency_tree_t) + call deps%add(nodes, error) + if (allocated(error)) return + + if (deps%finished()) then + call test_failed(error, "Newly added nodes cannot be already resolved") + return + end if + + if (deps%ndep /= 4) then + call test_failed(error, "Expected for dependencies in tree") + return + end if - call deps%resolve(".", error) - if (allocated(error)) return + call deps%resolve(".", error) + if (allocated(error)) return - if (.not. deps%finished()) then - call test_failed(error, "Mocked dependency tree must resolve in one step") - return - end if + if (.not. deps%finished()) then + call test_failed(error, "Mocked dependency tree must resolve in one step") + return + end if - end subroutine test_add_dependencies + end subroutine test_add_dependencies - subroutine registry_dir_not_found(error) - type(error_t), allocatable, intent(out) :: error + subroutine registry_dir_not_found(error) + type(error_t), allocatable, intent(out) :: error - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache')) ! Missing directories for namesapce and package name + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache')) ! Missing directories for namesapce and package name - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') - - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call delete_tmp_folder + call delete_tmp_folder - end subroutine registry_dir_not_found + end subroutine registry_dir_not_found - subroutine no_versions_in_registry(error) - type(error_t), allocatable, intent(out) :: error + subroutine no_versions_in_registry(error) + type(error_t), allocatable, intent(out) :: error - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return - - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) - - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) + + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call delete_tmp_folder + call delete_tmp_folder - end subroutine no_versions_in_registry + end subroutine no_versions_in_registry - subroutine version_not_found_in_registry(error) - type(error_t), allocatable, intent(out) :: error + subroutine version_not_found_in_registry(error) + type(error_t), allocatable, intent(out) :: error - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '0.1.0') + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '0.1.0') - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.9')) - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.1')) + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.9')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.1')) - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call delete_tmp_folder + call delete_tmp_folder - end subroutine version_not_found_in_registry + end subroutine version_not_found_in_registry - subroutine version_found_in_registry(error) - type(error_t), allocatable, intent(out) :: error + subroutine version_found_in_registry(error) + type(error_t), allocatable, intent(out) :: error - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd - type(toml_table), pointer :: child + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '0.1.0') + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '0.1.0') - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.2.0')) + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.2.0')) - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call get_current_directory(cwd, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'))) then - call test_failed(error, 'target_dir not set correctly') - call delete_tmp_folder; return - end if + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'))) then + call test_failed(error, 'target_dir not set correctly') + call delete_tmp_folder; return + end if - call delete_tmp_folder + call delete_tmp_folder - end subroutine version_found_in_registry - - subroutine not_a_dir(error) - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child - - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return - - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) - call filewrite(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'), ['']) ! File, not directory - - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') - - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call delete_tmp_folder + end subroutine version_found_in_registry + + subroutine not_a_dir(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) + call filewrite(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'), ['']) ! File, not directory + + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call delete_tmp_folder - end subroutine not_a_dir - - subroutine newest_version_in_registry(error) - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd - type(toml_table), pointer :: child - - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + end subroutine not_a_dir + + subroutine newest_version_in_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0')) - call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) - - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'cache') - - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call get_current_directory(cwd, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'))) then - call test_failed(error, 'target_dir not set correctly: '//target_dir//"'") - call delete_tmp_folder; return - end if - - call delete_tmp_folder + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) + + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'))) then + call test_failed(error, 'target_dir not set correctly: '//target_dir//"'") + call delete_tmp_folder; return + end if + + call delete_tmp_folder - end subroutine newest_version_in_registry - - !> No cache_path specified, use default cache path but folder exists already. - subroutine default_cache_path(error) - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child + end subroutine newest_version_in_registry + + !> No cache_path specified, use default cache path but folder exists already. + subroutine default_cache_path(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child - call new_table(table) - table%key = 'version-f' - call set_value(table, 'namespace', 'minhdao') - - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return - - call delete_tmp_folder - call mkdir(tmp_folder) - - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call new_table(table) - call add_table(table, 'registry', child) ! No cache_path specified, use default - - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - if (global_settings%registry_settings%cache_path /= & - & join_path(global_settings%path_to_config_folder, 'dependencies')) then - call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") - call delete_tmp_folder; return - end if - - if (.not. exists(global_settings%registry_settings%cache_path)) then - call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") - call delete_tmp_folder; return - end if + call new_table(table) + table%key = 'version-f' + call set_value(table, 'namespace', 'minhdao') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(tmp_folder) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call new_table(table) + call add_table(table, 'registry', child) ! No cache_path specified, use default + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (global_settings%registry_settings%cache_path /= & + & join_path(global_settings%path_to_config_folder, 'dependencies')) then + call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") + call delete_tmp_folder; return + end if + + if (.not. exists(global_settings%registry_settings%cache_path)) then + call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") + call delete_tmp_folder; return + end if - call delete_tmp_folder - - end subroutine default_cache_path + call delete_tmp_folder + + end subroutine default_cache_path - subroutine version_found_in_cache(error) - type(error_t), allocatable, intent(out) :: error - - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd, path - type(toml_table), pointer :: child - - call new_table(table) - table%key = 'test-dep' - call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '2.3.0') + subroutine version_found_in_cache(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd, path + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '2.3.0') - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - path = join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0') - call mkdir(path) - call filewrite(join_path(path, 'fpm.toml'), ['']) + call delete_tmp_folder + path = join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0') + call mkdir(path) + call filewrite(join_path(path, 'fpm.toml'), ['']) - call new_table(table) - call add_table(table, 'registry', child) + call new_table(table) + call add_table(table, 'registry', child) - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_current_directory(cwd, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0'))) then - call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") - call delete_tmp_folder; return - end if - - call delete_tmp_folder - - end subroutine version_found_in_cache - - subroutine no_version_in_default_cache(error) - type(error_t), allocatable, intent(out) :: error + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0'))) then + call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") + call delete_tmp_folder; return + end if + + call delete_tmp_folder + + end subroutine version_found_in_cache + + subroutine no_version_in_default_cache(error) + type(error_t), allocatable, intent(out) :: error - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child - call new_table(table) - table%key = 'version-f' - call set_value(table, 'namespace', 'minhdao') - call set_value(table, 'v', '0.1.0') - - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + call new_table(table) + table%key = 'version-f' + call set_value(table, 'namespace', 'minhdao') + call set_value(table, 'v', '0.1.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - call mkdir(tmp_folder) ! Dependencies folder doesn't exist + call delete_tmp_folder + call mkdir(tmp_folder) ! Dependencies folder doesn't exist - call new_table(table) - call add_table(table, 'registry', child) + call new_table(table) + call add_table(table, 'registry', child) - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call delete_tmp_folder + call delete_tmp_folder - end subroutine no_version_in_default_cache + end subroutine no_version_in_default_cache - subroutine other_versions_in_default_cache(error) - type(error_t), allocatable, intent(out) :: error + subroutine other_versions_in_default_cache(error) + type(error_t), allocatable, intent(out) :: error - type(toml_table) :: table - type(dependency_node_t) :: node - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir - type(toml_table), pointer :: child + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child - call new_table(table) - table%key = 'version-f' - call set_value(table, 'namespace', 'minhdao') - call set_value(table, 'v', '0.1.0') + call new_table(table) + table%key = 'version-f' + call set_value(table, 'namespace', 'minhdao') + call set_value(table, 'v', '0.1.0') - call new_dependency(node%dependency_config_t, table, error=error) - if (allocated(error)) return + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.1.0')) - call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '9.1.0')) + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.1.0')) + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '9.1.0')) - call new_table(table) - call add_table(table, 'registry', child) + call new_table(table) + call add_table(table, 'registry', child) - call setup_global_settings(global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call get_registry_settings(child, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call node%get_from_registry(target_dir, global_settings, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if - call delete_tmp_folder + call delete_tmp_folder - end subroutine other_versions_in_default_cache + end subroutine other_versions_in_default_cache - !> Resolve a single dependency node - subroutine resolve_dependency_once(self, dependency, root, error) - !> Mock instance of the dependency tree - class(mock_dependency_tree_t), intent(inout) :: self - !> Dependency configuration to add - type(dependency_node_t), intent(inout) :: dependency - !> Current installation prefix - character(len=*), intent(in) :: root - !> Error handling - type(error_t), allocatable, intent(out) :: error + !> Resolve a single dependency node + subroutine resolve_dependency_once(self, dependency, root, error) + !> Mock instance of the dependency tree + class(mock_dependency_tree_t), intent(inout) :: self + !> Dependency configuration to add + type(dependency_node_t), intent(inout) :: dependency + !> Current installation prefix + character(len=*), intent(in) :: root + !> Error handling + type(error_t), allocatable, intent(out) :: error - if (dependency%done) then - call test_failed(error, "Should only visit this node once") - return - end if + if (dependency%done) then + call test_failed(error, "Should only visit this node once") + return + end if - dependency%done = .true. + dependency%done = .true. - end subroutine resolve_dependency_once + end subroutine resolve_dependency_once - subroutine delete_tmp_folder - if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - end + subroutine delete_tmp_folder + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + end - subroutine setup_global_settings(global_settings, error) - type(fpm_global_settings), intent(out) :: global_settings - type(error_t), allocatable, intent(out) :: error + subroutine setup_global_settings(global_settings, error) + type(fpm_global_settings), intent(out) :: global_settings + type(error_t), allocatable, intent(out) :: error - character(:), allocatable :: cwd + character(:), allocatable :: cwd - call get_current_directory(cwd, error) - if (allocated(error)) return + call get_current_directory(cwd, error) + if (allocated(error)) return - global_settings%path_to_config_folder = join_path(cwd, tmp_folder) - global_settings%config_file_name = config_file_name - end subroutine + global_settings%path_to_config_folder = join_path(cwd, tmp_folder) + global_settings%config_file_name = config_file_name + end subroutine end module test_package_dependencies From 83ad3c0be06cf85a112ef5ce78b1544148309280 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 2 Mar 2023 19:51:56 +0100 Subject: [PATCH 119/799] Implement downloader_t and mock it for testing --- src/fpm/dependency.f90 | 2 +- test/fpm_test/test_package_dependencies.f90 | 48 ++++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 902a3cbb60..7a61b36a8d 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -511,7 +511,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade type(error_t), allocatable, intent(out) :: error !> Downloader instance. - type(downloader_t), optional, intent(in) :: downloader + class(downloader_t), optional, intent(in) :: downloader character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, status_code, downloaded_version type(version_t) :: version diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index e60cddf8f3..89fb9c9c2e 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -9,6 +9,7 @@ module test_package_dependencies use fpm_manifest_dependency use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings + use fpm_downloader, only: downloader_t implicit none private @@ -20,9 +21,14 @@ module test_package_dependencies type, extends(dependency_tree_t) :: mock_dependency_tree_t contains - procedure :: resolve_dependency => resolve_dependency_once + procedure, private :: resolve_dependency => resolve_dependency_once end type mock_dependency_tree_t + type, extends(downloader_t) :: mock_downloader_t + contains + procedure, nopass :: get => get_mock_package, unpack => unpack_mock_package + end type mock_downloader_t + contains !> Collect all exported unit tests @@ -514,6 +520,7 @@ subroutine default_cache_path(error) type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir type(toml_table), pointer :: child + type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'version-f' @@ -538,7 +545,7 @@ subroutine default_cache_path(error) call delete_tmp_folder; return end if - call node%get_from_registry(target_dir, global_settings, error) + call node%get_from_registry(target_dir, global_settings, error, mock_downloader) if (allocated(error)) then call delete_tmp_folder; return end if @@ -620,6 +627,7 @@ subroutine no_version_in_default_cache(error) type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir type(toml_table), pointer :: child + type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'version-f' @@ -645,7 +653,7 @@ subroutine no_version_in_default_cache(error) call delete_tmp_folder; return end if - call node%get_from_registry(target_dir, global_settings, error) + call node%get_from_registry(target_dir, global_settings, error, mock_downloader) if (allocated(error)) then call delete_tmp_folder; return end if @@ -662,6 +670,7 @@ subroutine other_versions_in_default_cache(error) type(fpm_global_settings) :: global_settings character(len=:), allocatable :: target_dir type(toml_table), pointer :: child + type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'version-f' @@ -688,7 +697,7 @@ subroutine other_versions_in_default_cache(error) call delete_tmp_folder; return end if - call node%get_from_registry(target_dir, global_settings, error) + call node%get_from_registry(target_dir, global_settings, error, mock_downloader) if (allocated(error)) then call delete_tmp_folder; return end if @@ -732,6 +741,35 @@ subroutine setup_global_settings(global_settings, error) global_settings%path_to_config_folder = join_path(cwd, tmp_folder) global_settings%config_file_name = config_file_name - end subroutine + end + + subroutine get_mock_package(url, tmp_file, error) + character(*), intent(in) :: url + character(*), intent(in) :: tmp_file + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + call execute_command_line("echo '"//'{"code": 200, "version": "0.1.0", "tar": "abc"}'// & + & "' > "//tmp_file, exitstat=stat) + + if (stat /= 0) then + call test_failed(error, "Failed to create mock package"); return + end if + end + + subroutine unpack_mock_package(tmp_file, destination, error) + character(*), intent(in) :: tmp_file + character(*), intent(in) :: destination + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + call execute_command_line('cp '//tmp_file//' '//destination, exitstat=stat) + + if (stat /= 0) then + call test_failed(error, "Failed to create mock package"); return + end if + end end module test_package_dependencies From 57b8813f108ede366c296a4bd3882f7b6a949b07 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 2 Mar 2023 19:58:51 +0100 Subject: [PATCH 120/799] Use more generic example names --- test/fpm_test/test_package_dependencies.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 89fb9c9c2e..f9b1c35a8b 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -523,8 +523,8 @@ subroutine default_cache_path(error) type(mock_downloader_t) :: mock_downloader call new_table(table) - table%key = 'version-f' - call set_value(table, 'namespace', 'minhdao') + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return @@ -630,8 +630,8 @@ subroutine no_version_in_default_cache(error) type(mock_downloader_t) :: mock_downloader call new_table(table) - table%key = 'version-f' - call set_value(table, 'namespace', 'minhdao') + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) @@ -673,8 +673,8 @@ subroutine other_versions_in_default_cache(error) type(mock_downloader_t) :: mock_downloader call new_table(table) - table%key = 'version-f' - call set_value(table, 'namespace', 'minhdao') + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) From 4da8c1283fe81787446fd2cbcb55df4c3d698511 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 3 Mar 2023 02:25:46 +0100 Subject: [PATCH 121/799] Avoid command line executions --- src/fpm/dependency.f90 | 17 ++++++--------- src/fpm/downloader.f90 | 19 ++++++++++++++-- test/fpm_test/test_package_dependencies.f90 | 24 ++++++++++++++------- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 7a61b36a8d..4489cb4d54 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -68,7 +68,7 @@ module fpm_dependency use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_downloader, only: downloader_t - use json_module + use json_module, only: json_file implicit none private @@ -551,21 +551,18 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (allocated(self%requested_version)) then ! Request specific version. - call downloader%get(target_url//'/'//self%requested_version%s(), tmp_file, error) + call downloader%get_pkg_data(target_url//'/'//self%requested_version%s(), tmp_file, j_pkg, error) else ! Request latest version. - call downloader%get(target_url, tmp_file, error) + call downloader%get_pkg_data(target_url, tmp_file, j_pkg, error) end if + close (unit, status='delete') + if (allocated(error)) then - close (unit, status='delete'); return + call j_pkg%destroy(); return end if - call j_pkg%initialize() - call j_pkg%load_file(tmp_file) - - close (unit, status='delete') - if (j_pkg%failed()) then call fatal_error(error, "Error reading package data of '"//join_path(self%namespace, self%name)//"'.") call j_pkg%destroy(); return @@ -617,7 +614,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end if print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." - call downloader%get(target_url, tmp_file, error) + call downloader%get_file(target_url, tmp_file, error) if (allocated(error)) then close (unit, status='delete'); return diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 1ccf342098..28bd07ea68 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,6 +1,7 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error use fpm_filesystem, only: which + use json_module, only: json_file implicit none private @@ -10,13 +11,27 @@ module fpm_downloader !> This type could be entirely avoided but it is quite practical because it can be mocked for testing. type downloader_t contains - procedure, nopass :: get, unpack + procedure, nopass :: get_pkg_data, get_file, unpack end type contains !> Perform an http get request and save output to file. - subroutine get(url, tmp_file, error) + subroutine get_pkg_data(url, tmp_file, json, error) + character(*), intent(in) :: url + character(*), intent(in) :: tmp_file + type(json_file), intent(out) :: json + type(error_t), allocatable, intent(out) :: error + + call json%initialize() + + call get_file(url, tmp_file, error) + if (allocated(error)) return + + call json%load_file(tmp_file) + end + + subroutine get_file(url, tmp_file, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_file type(error_t), allocatable, intent(out) :: error diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index f9b1c35a8b..aed64a6092 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -10,6 +10,7 @@ module test_package_dependencies use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings use fpm_downloader, only: downloader_t + use json_module, only: json_file, json_value, json_core implicit none private @@ -26,7 +27,7 @@ module test_package_dependencies type, extends(downloader_t) :: mock_downloader_t contains - procedure, nopass :: get => get_mock_package, unpack => unpack_mock_package + procedure, nopass :: get_pkg_data, get_file, unpack => unpack_mock_package end type mock_downloader_t contains @@ -743,19 +744,26 @@ subroutine setup_global_settings(global_settings, error) global_settings%config_file_name = config_file_name end - subroutine get_mock_package(url, tmp_file, error) + subroutine get_pkg_data(url, tmp_file, json, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_file + type(json_file), intent(out) :: json type(error_t), allocatable, intent(out) :: error - integer :: stat + type(json_core) :: core + type(json_value), pointer :: p - call execute_command_line("echo '"//'{"code": 200, "version": "0.1.0", "tar": "abc"}'// & - & "' > "//tmp_file, exitstat=stat) + call core%create_object(p, '') + call core%add(p, 'code', '200') + call core%add(p, 'version', '0.0.1') + call core%add(p, 'tar', 'abc') + call json%json_file_add(p) + end - if (stat /= 0) then - call test_failed(error, "Failed to create mock package"); return - end if + subroutine get_file(url, tmp_file, error) + character(*), intent(in) :: url + character(*), intent(in) :: tmp_file + type(error_t), allocatable, intent(out) :: error end subroutine unpack_mock_package(tmp_file, destination, error) From be98b8838b19af641514b63550a203800d932029 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 3 Mar 2023 03:35:29 +0100 Subject: [PATCH 122/799] Try import entire json_module --- src/fpm/dependency.f90 | 2 +- src/fpm/downloader.f90 | 2 +- test/fpm_test/test_package_dependencies.f90 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 4489cb4d54..5175190a76 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -68,7 +68,7 @@ module fpm_dependency use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_downloader, only: downloader_t - use json_module, only: json_file + use json_module implicit none private diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 28bd07ea68..a8a307835c 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,7 +1,7 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error use fpm_filesystem, only: which - use json_module, only: json_file + use json_module implicit none private diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index aed64a6092..9519089060 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -10,7 +10,7 @@ module test_package_dependencies use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings use fpm_downloader, only: downloader_t - use json_module, only: json_file, json_value, json_core + use json_module implicit none private From 8e89d62fa6d108be968bec3b8ad3e0383889f38d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 3 Mar 2023 03:38:47 +0100 Subject: [PATCH 123/799] Revert --- src/fpm/dependency.f90 | 2 +- src/fpm/downloader.f90 | 2 +- test/fpm_test/test_package_dependencies.f90 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 5175190a76..4489cb4d54 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -68,7 +68,7 @@ module fpm_dependency use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_downloader, only: downloader_t - use json_module + use json_module, only: json_file implicit none private diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index a8a307835c..28bd07ea68 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,7 +1,7 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error use fpm_filesystem, only: which - use json_module + use json_module, only: json_file implicit none private diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 9519089060..aed64a6092 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -10,7 +10,7 @@ module test_package_dependencies use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings use fpm_downloader, only: downloader_t - use json_module + use json_module, only: json_file, json_value, json_core implicit none private From ff26d11aef7d6ff3a74b6cbf708f696ddbfdf562 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 4 Mar 2023 03:35:51 +0100 Subject: [PATCH 124/799] Bump toml-f, swap json-fortran with jonquil, check if keys exist before reading --- fpm.toml | 8 +-- src/fpm/dependency.f90 | 66 ++++++++++----------- src/fpm/downloader.f90 | 11 ++-- src/fpm/toml.f90 | 2 + src/fpm_settings.f90 | 27 +++++---- test/fpm_test/test_package_dependencies.f90 | 13 +--- 6 files changed, 64 insertions(+), 63 deletions(-) diff --git a/fpm.toml b/fpm.toml index 1973f99c87..2a02e53aaa 100644 --- a/fpm.toml +++ b/fpm.toml @@ -8,15 +8,15 @@ copyright = "2020 fpm contributors" [dependencies] [dependencies.toml-f] git = "https://github.com/toml-f/toml-f" -rev = "aee54c5a480d623af99828c76df0447a15ce90dc" +rev = "963082b9a1ef1833c2cd2d29dc037dbdb42d4b84" [dependencies.M_CLI2] git = "https://github.com/urbanjost/M_CLI2.git" rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" -[dependencies.json-fortran] -git = "https://github.com/jacobwilliams/json-fortran.git" -rev = "3ab8f98209871875325c6985dd0e50085d1c82c2" +[dependencies.jonquil] +git = "https://github.com/toml-f/jonquil" +rev = "93354799980556023442b2307010c600370af097" [[test]] name = "cli-test" diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 4489cb4d54..abc1b2c388 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -68,7 +68,7 @@ module fpm_dependency use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_downloader, only: downloader_t - use json_module, only: json_file + use jonquil, only: json_object, json_value, cast_to_object implicit none private @@ -513,11 +513,11 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade !> Downloader instance. class(downloader_t), optional, intent(in) :: downloader - character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, status_code, downloaded_version + character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, downloaded_version, code_str type(version_t) :: version - integer :: stat, unit - type(json_file) :: j_pkg - logical :: is_found + integer :: stat, unit, code + class(json_value), allocatable :: j_value + type(json_object), pointer :: json ! Use local registry if it was specified in the global config file. if (allocated(global_settings%registry_settings%path)) then @@ -551,62 +551,62 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (allocated(self%requested_version)) then ! Request specific version. - call downloader%get_pkg_data(target_url//'/'//self%requested_version%s(), tmp_file, j_pkg, error) + call downloader%get_pkg_data(target_url//'/'//self%requested_version%s(), tmp_file, j_value, error) else ! Request latest version. - call downloader%get_pkg_data(target_url, tmp_file, j_pkg, error) + call downloader%get_pkg_data(target_url, tmp_file, j_value, error) end if close (unit, status='delete') + if (allocated(error)) return - if (allocated(error)) then - call j_pkg%destroy(); return + json => cast_to_object(j_value) + if (.not. associated(json)) then + call fatal_error(error, "Error parsing JSON from '"//target_url//"'."); return end if - if (j_pkg%failed()) then - call fatal_error(error, "Error reading package data of '"//join_path(self%namespace, self%name)//"'.") - call j_pkg%destroy(); return + if (.not. json%has_key('code')) then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No status code."); return end if - call j_pkg%get('code', status_code, is_found) + call get_value(json, 'code', code, stat=stat) - if (.not. is_found) then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No status code.") - call j_pkg%destroy(); return + if (code /= 200 .or. stat /= 0) then + allocate (character(int(log10(real(code))) + 1) :: code_str) + write (code_str, '(I0)') code + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': " & + & //"Status code '"//code_str//"'."); return end if - if (status_code /= '200') then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': " & - & //"Status code '"//status_code//"'.") - call j_pkg%destroy(); return + if (.not. json%has_key('tar')) then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No download link."); return end if ! Get download link and version of the package. - call j_pkg%get('tar', target_url, is_found) + call get_value(json, 'tar', target_url, stat=stat) - if (.not. is_found) then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No download link.") - call j_pkg%destroy(); return + if (stat /= 0) then + call fatal_error(error, "Failed to get download link from '"//join_path(self%namespace, self%name)//"'."); return + end if + + if (.not. json%has_key('version')) then + call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No version found."); return end if ! Get version of the package. - call j_pkg%get('version', downloaded_version, is_found) + call get_value(json, 'version', downloaded_version, stat=stat) - if (.not. is_found) then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No version.") - call j_pkg%destroy(); return + if (stat /= 0) then + call fatal_error(error, "Failed to download version from '"//join_path(self%namespace, self%name)//"'."); return end if call new_version(version, downloaded_version, error) if (allocated(error)) then - call fatal_error(error, "Version not valid: '"//downloaded_version//"'.") - call j_pkg%destroy(); return + call fatal_error(error, "Version not valid: '"//downloaded_version//"'."); return end if - call j_pkg%destroy() - - ! Open new temporary file for downloading the actual package. + ! Open new tmp file for downloading the actual package. open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) if (stat /= 0) then diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 28bd07ea68..0c12c6e6bc 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,7 +1,7 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error use fpm_filesystem, only: which - use json_module, only: json_file + use jonquil, only: json_value, json_error, json_load implicit none private @@ -20,15 +20,18 @@ module fpm_downloader subroutine get_pkg_data(url, tmp_file, json, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_file - type(json_file), intent(out) :: json + class(json_value), allocatable, intent(out) :: json type(error_t), allocatable, intent(out) :: error - call json%initialize() + class(json_error), allocatable :: j_error call get_file(url, tmp_file, error) if (allocated(error)) return - call json%load_file(tmp_file) + call json_load(json, tmp_file, error=j_error) + if (allocated(j_error)) then + allocate (error); call move_alloc(j_error%message, error%message); call json%destroy(); return + end if end subroutine get_file(url, tmp_file, error) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 5d87b20673..a873d803b1 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -80,6 +80,8 @@ subroutine get_list(table, key, list, error) type(toml_array), pointer :: children character(len=:), allocatable :: str + if (.not.table%has_key(key)) return + call get_value(table, key, children, requested=.false.) if (associated(children)) then nlist = len(children) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 5e5e137236..2c1d07313d 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -134,10 +134,11 @@ subroutine get_registry_settings(table, global_settings, error) allocate (global_settings%registry_settings) - call get_value(table, 'path', path, stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry path: '"//path//"'."); return + if (table%has_key('path')) then + call get_value(table, 'path', path, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading registry path: '"//path//"'."); return + end if end if if (allocated(path)) then @@ -157,10 +158,11 @@ subroutine get_registry_settings(table, global_settings, error) end if end if - call get_value(table, 'url', url, stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry url: '"//url//"'."); return + if (table%has_key('url')) then + call get_value(table, 'url', url, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading registry url: '"//url//"'."); return + end if end if if (allocated(url)) then @@ -173,10 +175,11 @@ subroutine get_registry_settings(table, global_settings, error) global_settings%registry_settings%url = official_registry_base_url end if - call get_value(table, 'cache_path', cache_path, stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'."); return + if (table%has_key('cache_path')) then + call get_value(table, 'cache_path', cache_path, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'."); return + end if end if if (allocated(cache_path)) then diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index aed64a6092..d8696d9542 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -10,7 +10,7 @@ module test_package_dependencies use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings use fpm_downloader, only: downloader_t - use json_module, only: json_file, json_value, json_core + use jonquil, only: json_object, json_value, json_loads, cast_to_object implicit none private @@ -747,17 +747,10 @@ subroutine setup_global_settings(global_settings, error) subroutine get_pkg_data(url, tmp_file, json, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_file - type(json_file), intent(out) :: json + class(json_value), allocatable, intent(out) :: json type(error_t), allocatable, intent(out) :: error - type(json_core) :: core - type(json_value), pointer :: p - - call core%create_object(p, '') - call core%add(p, 'code', '200') - call core%add(p, 'version', '0.0.1') - call core%add(p, 'tar', 'abc') - call json%json_file_add(p) + call json_loads(json, '{"code": 200, "version": "0.0.1", "tar": "abc"}') end subroutine get_file(url, tmp_file, error) From 8f979a6a07163288b028db449656e5fc765b27c1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 4 Mar 2023 15:06:57 +0100 Subject: [PATCH 125/799] Reformat with indentation width 2 --- src/fpm_settings.f90 | 426 +++++++------- test/fpm_test/test_settings.f90 | 998 ++++++++++++++++---------------- 2 files changed, 712 insertions(+), 712 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 2c1d07313d..f3b5e9421d 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -1,222 +1,222 @@ !> Manages global settings which are defined in the global config file. module fpm_settings - use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path, mkdir - use fpm_environment, only: os_is_unix - use fpm_error, only: error_t, fatal_error - use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load, check_keys - use fpm_os, only: get_current_directory, change_directory, get_absolute_path, & - convert_to_absolute_path - implicit none - private - public :: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url - - character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' - - type :: fpm_global_settings - !> Path to the global config file excluding the file name. - character(len=:), allocatable :: path_to_config_folder - !> Name of the global config file. The default is `config.toml`. - character(len=:), allocatable :: config_file_name - !> Registry configs. - type(fpm_registry_settings), allocatable :: registry_settings - contains - procedure :: has_custom_location, full_path - end type - - type :: fpm_registry_settings - !> The path to the local registry. If allocated, the local registry - !> will be used instead of the remote registry and replaces the - !> local cache. - character(len=:), allocatable :: path - !> The URL to the remote registry. Can be used to get packages - !> from the official or a custom registry. - character(len=:), allocatable :: url - !> The path to the cache folder. If not specified, the default cache - !> folders are `~/.local/share/fpm/dependencies` on Unix and - !> `%APPDATA%\local\fpm\dependencies` on Windows. - !> Cannot be used together with `path`. - character(len=:), allocatable :: cache_path - end type + use fpm_filesystem, only: exists, join_path, get_local_prefix, is_absolute_path, mkdir + use fpm_environment, only: os_is_unix + use fpm_error, only: error_t, fatal_error + use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load, check_keys + use fpm_os, only: get_current_directory, change_directory, get_absolute_path, & + convert_to_absolute_path + implicit none + private + public :: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url + + character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' + + type :: fpm_global_settings + !> Path to the global config file excluding the file name. + character(len=:), allocatable :: path_to_config_folder + !> Name of the global config file. The default is `config.toml`. + character(len=:), allocatable :: config_file_name + !> Registry configs. + type(fpm_registry_settings), allocatable :: registry_settings + contains + procedure :: has_custom_location, full_path + end type + + type :: fpm_registry_settings + !> The path to the local registry. If allocated, the local registry + !> will be used instead of the remote registry and replaces the + !> local cache. + character(len=:), allocatable :: path + !> The URL to the remote registry. Can be used to get packages + !> from the official or a custom registry. + character(len=:), allocatable :: url + !> The path to the cache folder. If not specified, the default cache + !> folders are `~/.local/share/fpm/dependencies` on Unix and + !> `%APPDATA%\local\fpm\dependencies` on Windows. + !> Cannot be used together with `path`. + character(len=:), allocatable :: cache_path + end type contains - !> Obtain global settings from the global config file. - subroutine get_global_settings(global_settings, error) - !> Global settings to be obtained. - type(fpm_global_settings), intent(inout) :: global_settings - !> Error reading config file. - type(error_t), allocatable, intent(out) :: error - !> TOML table to be filled with global config settings. - type(toml_table), allocatable :: table - !> Error parsing to TOML table. - type(toml_error), allocatable :: parse_error - - type(toml_table), pointer :: registry_table - integer :: stat - - ! Use custom path to the config file if it was specified. - if (global_settings%has_custom_location()) then - ! Throw error if folder doesn't exist. - if (.not. exists(global_settings%path_to_config_folder)) then - call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'."); return - end if - - ! Throw error if the file doesn't exist. - if (.not. exists(global_settings%full_path())) then - call fatal_error(error, "File not found: '"//global_settings%full_path()//"'."); return - end if - - ! Make sure that the path to the global config file is absolute. - call convert_to_absolute_path(global_settings%path_to_config_folder, error) - if (allocated(error)) return - else - ! Use default path if it wasn't specified. - if (os_is_unix()) then - global_settings%path_to_config_folder = join_path(get_local_prefix(), 'share', 'fpm') - else - global_settings%path_to_config_folder = join_path(get_local_prefix(), 'fpm') - end if - - ! Use default file name. - global_settings%config_file_name = 'config.toml' - - ! Return if config file doesn't exist. - if (.not. exists(global_settings%full_path())) return - end if - - ! Load into TOML table. - call toml_load(table, global_settings%full_path(), error=parse_error) - - if (allocated(parse_error)) then - allocate (error); call move_alloc(parse_error%message, error%message); return - end if - - call get_value(table, 'registry', registry_table, requested=.false., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry from config file '"// & - & global_settings%full_path()//"'."); return - end if - - ! A registry table was found. - if (associated(registry_table)) then - call get_registry_settings(registry_table, global_settings, error); return - else - ! No registry table was found, use default settings for url and cache_path. - allocate (global_settings%registry_settings) - global_settings%registry_settings%url = official_registry_base_url - global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & - & 'dependencies'); return - end if - - end subroutine get_global_settings - - !> Read registry settings from the global config file. - subroutine get_registry_settings(table, global_settings, error) - !> The [registry] subtable from the global config file. - type(toml_table), target, intent(inout) :: table - !> The global settings which can be filled with the registry settings. - type(fpm_global_settings), intent(inout) :: global_settings - !> Error handling. - type(error_t), allocatable, intent(out) :: error - - character(:), allocatable :: path, url, cache_path - integer :: stat - - !> List of valid keys for the dependency table. - character(*), dimension(*), parameter :: valid_keys = [character(10) :: & - & 'path', & - & 'url', & - & 'cache_path' & - & ] - - call check_keys(table, valid_keys, error) + !> Obtain global settings from the global config file. + subroutine get_global_settings(global_settings, error) + !> Global settings to be obtained. + type(fpm_global_settings), intent(inout) :: global_settings + !> Error reading config file. + type(error_t), allocatable, intent(out) :: error + !> TOML table to be filled with global config settings. + type(toml_table), allocatable :: table + !> Error parsing to TOML table. + type(toml_error), allocatable :: parse_error + + type(toml_table), pointer :: registry_table + integer :: stat + + ! Use custom path to the config file if it was specified. + if (global_settings%has_custom_location()) then + ! Throw error if folder doesn't exist. + if (.not. exists(global_settings%path_to_config_folder)) then + call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'."); return + end if + + ! Throw error if the file doesn't exist. + if (.not. exists(global_settings%full_path())) then + call fatal_error(error, "File not found: '"//global_settings%full_path()//"'."); return + end if + + ! Make sure that the path to the global config file is absolute. + call convert_to_absolute_path(global_settings%path_to_config_folder, error) + if (allocated(error)) return + else + ! Use default path if it wasn't specified. + if (os_is_unix()) then + global_settings%path_to_config_folder = join_path(get_local_prefix(), 'share', 'fpm') + else + global_settings%path_to_config_folder = join_path(get_local_prefix(), 'fpm') + end if + + ! Use default file name. + global_settings%config_file_name = 'config.toml' + + ! Return if config file doesn't exist. + if (.not. exists(global_settings%full_path())) return + end if + + ! Load into TOML table. + call toml_load(table, global_settings%full_path(), error=parse_error) + + if (allocated(parse_error)) then + allocate (error); call move_alloc(parse_error%message, error%message); return + end if + + call get_value(table, 'registry', registry_table, requested=.false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading registry from config file '"// & + & global_settings%full_path()//"'."); return + end if + + ! A registry table was found. + if (associated(registry_table)) then + call get_registry_settings(registry_table, global_settings, error); return + else + ! No registry table was found, use default settings for url and cache_path. + allocate (global_settings%registry_settings) + global_settings%registry_settings%url = official_registry_base_url + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & + & 'dependencies'); return + end if + + end subroutine get_global_settings + + !> Read registry settings from the global config file. + subroutine get_registry_settings(table, global_settings, error) + !> The [registry] subtable from the global config file. + type(toml_table), target, intent(inout) :: table + !> The global settings which can be filled with the registry settings. + type(fpm_global_settings), intent(inout) :: global_settings + !> Error handling. + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: path, url, cache_path + integer :: stat + + !> List of valid keys for the dependency table. + character(*), dimension(*), parameter :: valid_keys = [character(10) :: & + & 'path', & + & 'url', & + & 'cache_path' & + & ] + + call check_keys(table, valid_keys, error) + if (allocated(error)) return + + allocate (global_settings%registry_settings) + + if (table%has_key('path')) then + call get_value(table, 'path', path, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading registry path: '"//path//"'."); return + end if + end if + + if (allocated(path)) then + if (is_absolute_path(path)) then + global_settings%registry_settings%path = path + else + ! Get canonical, absolute path on both Unix and Windows. + call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & + & global_settings%registry_settings%path, error) if (allocated(error)) return - allocate (global_settings%registry_settings) - - if (table%has_key('path')) then - call get_value(table, 'path', path, stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry path: '"//path//"'."); return - end if + ! Check if the path to the registry exists. + if (.not. exists(global_settings%registry_settings%path)) then + call fatal_error(error, "Directory '"//global_settings%registry_settings%path// & + & "' doesn't exist."); return end if - - if (allocated(path)) then - if (is_absolute_path(path)) then - global_settings%registry_settings%path = path - else - ! Get canonical, absolute path on both Unix and Windows. - call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & - & global_settings%registry_settings%path, error) - if (allocated(error)) return - - ! Check if the path to the registry exists. - if (.not. exists(global_settings%registry_settings%path)) then - call fatal_error(error, "Directory '"//global_settings%registry_settings%path// & - & "' doesn't exist."); return - end if - end if - end if - - if (table%has_key('url')) then - call get_value(table, 'url', url, stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading registry url: '"//url//"'."); return - end if - end if - - if (allocated(url)) then - ! Throw error when both path and url were provided. - if (allocated(path)) then - call fatal_error(error, 'Do not provide both path and url to the registry.'); return - end if - global_settings%registry_settings%url = url - else if (.not. allocated(path)) then - global_settings%registry_settings%url = official_registry_base_url - end if - - if (table%has_key('cache_path')) then - call get_value(table, 'cache_path', cache_path, stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'."); return - end if - end if - - if (allocated(cache_path)) then - ! Throw error when both path and cache_path were provided. - if (allocated(path)) then - call fatal_error(error, "Do not provide both 'path' and 'cache_path'."); return - end if - - if (is_absolute_path(cache_path)) then - if (.not. exists(cache_path)) call mkdir(cache_path) - global_settings%registry_settings%cache_path = cache_path - else - cache_path = join_path(global_settings%path_to_config_folder, cache_path) - if (.not. exists(cache_path)) call mkdir(cache_path) - ! Get canonical, absolute path on both Unix and Windows. - call get_absolute_path(cache_path, global_settings%registry_settings%cache_path, error) - if (allocated(error)) return - end if - else if (.not. allocated(path)) then - global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & - & 'dependencies') - end if - end subroutine get_registry_settings - - !> True if the global config file is not at the default location. - pure logical function has_custom_location(self) - class(fpm_global_settings), intent(in) :: self - - has_custom_location = allocated(self%path_to_config_folder) .and. allocated(self%config_file_name) - end function - - !> The full path to the global config file. - function full_path(self) result(result) - class(fpm_global_settings), intent(in) :: self - character(len=:), allocatable :: result - - result = join_path(self%path_to_config_folder, self%config_file_name) - end function + end if + end if + + if (table%has_key('url')) then + call get_value(table, 'url', url, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading registry url: '"//url//"'."); return + end if + end if + + if (allocated(url)) then + ! Throw error when both path and url were provided. + if (allocated(path)) then + call fatal_error(error, 'Do not provide both path and url to the registry.'); return + end if + global_settings%registry_settings%url = url + else if (.not. allocated(path)) then + global_settings%registry_settings%url = official_registry_base_url + end if + + if (table%has_key('cache_path')) then + call get_value(table, 'cache_path', cache_path, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Error reading path to registry cache: '"//cache_path//"'."); return + end if + end if + + if (allocated(cache_path)) then + ! Throw error when both path and cache_path were provided. + if (allocated(path)) then + call fatal_error(error, "Do not provide both 'path' and 'cache_path'."); return + end if + + if (is_absolute_path(cache_path)) then + if (.not. exists(cache_path)) call mkdir(cache_path) + global_settings%registry_settings%cache_path = cache_path + else + cache_path = join_path(global_settings%path_to_config_folder, cache_path) + if (.not. exists(cache_path)) call mkdir(cache_path) + ! Get canonical, absolute path on both Unix and Windows. + call get_absolute_path(cache_path, global_settings%registry_settings%cache_path, error) + if (allocated(error)) return + end if + else if (.not. allocated(path)) then + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & + & 'dependencies') + end if + end subroutine get_registry_settings + + !> True if the global config file is not at the default location. + pure logical function has_custom_location(self) + class(fpm_global_settings), intent(in) :: self + + has_custom_location = allocated(self%path_to_config_folder) .and. allocated(self%config_file_name) + end function + + !> The full path to the global config file. + function full_path(self) result(result) + class(fpm_global_settings), intent(in) :: self + character(len=:), allocatable :: result + + result = join_path(self%path_to_config_folder, self%config_file_name) + end function end module fpm_settings diff --git a/test/fpm_test/test_settings.f90 b/test/fpm_test/test_settings.f90 index c88bf6a5e9..a63b022cde 100644 --- a/test/fpm_test/test_settings.f90 +++ b/test/fpm_test/test_settings.f90 @@ -1,676 +1,676 @@ module test_settings - use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_settings, only: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url - use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists, get_local_prefix - use fpm_environment, only: os_is_unix - use fpm_toml, only: toml_table, new_table, add_table, set_value - use fpm_os, only: get_absolute_path, get_current_directory + use testsuite, only: new_unittest, unittest_t, error_t, test_failed + use fpm_settings, only: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url + use fpm_filesystem, only: is_dir, join_path, mkdir, filewrite, os_delete_dir, exists, get_local_prefix + use fpm_environment, only: os_is_unix + use fpm_toml, only: toml_table, new_table, add_table, set_value + use fpm_os, only: get_absolute_path, get_current_directory - implicit none - private - public :: collect_settings + implicit none + private + public :: collect_settings - character(len=*), parameter :: tmp_folder = 'tmp' - character(len=*), parameter :: config_file_name = 'config.toml' + character(len=*), parameter :: tmp_folder = 'tmp' + character(len=*), parameter :: config_file_name = 'config.toml' contains - !> Collect unit tests. - subroutine collect_settings(tests) - - !> Unit tests to collect. - type(unittest_t), allocatable, intent(out) :: tests(:) - - tests = [ & - & new_unittest('no-folder', no_folder, should_fail=.true.), & - & new_unittest('no-file', no_file, should_fail=.true.), & - & new_unittest('empty-file', empty_file), & - & new_unittest('default-config-settings', default_config_settings), & - & new_unittest('error-reading-table', error_reading_table, should_fail=.true.), & - & new_unittest('empty-registry-table', empty_registry_table), & - & new_unittest('invalid-key', invalid_key, should_fail=.true.), & - & new_unittest('invalid-type', invalid_type, should_fail=.true.), & - & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & - & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & - & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & - & new_unittest('relative-path-to-registry', relative_path_to_registry), & - & new_unittest('relative-path-to-registry-file-read', relative_path_to_registry_file_read), & - & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & - & new_unittest('has-url-to-registry', has_url_to_registry), & - & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.), & - & new_unittest('has-both-path-and-cache-path', has_both_path_and_cache_path, should_fail=.true.), & - & new_unittest('abs-cache-path-no-dir', abs_cache_path_no_dir), & - & new_unittest('abs-cache-path-has-dir', abs_cache_path_has_dir), & - & new_unittest('rel-cache-path-no-dir', rel_cache_path_no_dir), & - & new_unittest('rel-cache-path-has-dir', rel_cache_path_has_dir) & - ] + !> Collect unit tests. + subroutine collect_settings(tests) + + !> Unit tests to collect. + type(unittest_t), allocatable, intent(out) :: tests(:) + + tests = [ & + & new_unittest('no-folder', no_folder, should_fail=.true.), & + & new_unittest('no-file', no_file, should_fail=.true.), & + & new_unittest('empty-file', empty_file), & + & new_unittest('default-config-settings', default_config_settings), & + & new_unittest('error-reading-table', error_reading_table, should_fail=.true.), & + & new_unittest('empty-registry-table', empty_registry_table), & + & new_unittest('invalid-key', invalid_key, should_fail=.true.), & + & new_unittest('invalid-type', invalid_type, should_fail=.true.), & + & new_unittest('has-non-existent-path-to-registry', has_non_existent_path_to_registry, should_fail=.true.), & + & new_unittest('has-existent-path-to-registry', has_existent_path_to_registry), & + & new_unittest('absolute-path-to-registry', absolute_path_to_registry), & + & new_unittest('relative-path-to-registry', relative_path_to_registry), & + & new_unittest('relative-path-to-registry-file-read', relative_path_to_registry_file_read), & + & new_unittest('canonical-path-to-registry', canonical_path_to_registry), & + & new_unittest('has-url-to-registry', has_url_to_registry), & + & new_unittest('has-both-path-and-url-to-registry', has_both_path_and_url_to_registry, should_fail=.true.), & + & new_unittest('has-both-path-and-cache-path', has_both_path_and_cache_path, should_fail=.true.), & + & new_unittest('abs-cache-path-no-dir', abs_cache_path_no_dir), & + & new_unittest('abs-cache-path-has-dir', abs_cache_path_has_dir), & + & new_unittest('rel-cache-path-no-dir', rel_cache_path_no_dir), & + & new_unittest('rel-cache-path-has-dir', rel_cache_path_has_dir) & + ] - end subroutine collect_settings + end subroutine collect_settings - subroutine delete_tmp_folder - if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) - end subroutine + subroutine delete_tmp_folder + if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) + end subroutine - subroutine setup_global_settings(global_settings, error) - type(fpm_global_settings), intent(out) :: global_settings - type(error_t), allocatable, intent(out) :: error + subroutine setup_global_settings(global_settings, error) + type(fpm_global_settings), intent(out) :: global_settings + type(error_t), allocatable, intent(out) :: error - character(:), allocatable :: cwd + character(:), allocatable :: cwd - call get_current_directory(cwd, error) - if (allocated(error)) return + call get_current_directory(cwd, error) + if (allocated(error)) return - global_settings%path_to_config_folder = join_path(cwd, tmp_folder) - global_settings%config_file_name = config_file_name - end subroutine + global_settings%path_to_config_folder = join_path(cwd, tmp_folder) + global_settings%config_file_name = config_file_name + end subroutine - !> Throw error when custom path to config file was entered but no folder exists. - subroutine no_folder(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + !> Throw error when custom path to config file was entered but no folder exists. + subroutine no_folder(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings - call delete_tmp_folder + call delete_tmp_folder - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call get_global_settings(global_settings, error) - end subroutine + call get_global_settings(global_settings, error) + end subroutine - !> Throw error when custom path to config file was entered but no file exists. - subroutine no_file(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + !> Throw error when custom path to config file was entered but no file exists. + subroutine no_file(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call get_global_settings(global_settings, error) - end subroutine + call get_global_settings(global_settings, error) + end subroutine - !> No custom path and config file specified, use default path and file name. - subroutine default_config_settings(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + !> No custom path and config file specified, use default path and file name. + subroutine default_config_settings(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings - character(:), allocatable :: default_path + character(:), allocatable :: default_path - call delete_tmp_folder + call delete_tmp_folder - call get_global_settings(global_settings, error) - if (allocated(error)) return + call get_global_settings(global_settings, error) + if (allocated(error)) return - if (os_is_unix()) then - default_path = join_path(get_local_prefix(), 'share', 'fpm') - else - default_path = join_path(get_local_prefix(), 'fpm') - end if + if (os_is_unix()) then + default_path = join_path(get_local_prefix(), 'share', 'fpm') + else + default_path = join_path(get_local_prefix(), 'fpm') + end if - if (global_settings%path_to_config_folder /= default_path) then - call test_failed(error, "Path to config folder not set correctly :'"//global_settings%config_file_name//"'") - return - end if + if (global_settings%path_to_config_folder /= default_path) then + call test_failed(error, "Path to config folder not set correctly :'"//global_settings%config_file_name//"'") + return + end if - if (global_settings%config_file_name /= 'config.toml') then - call test_failed(error, "Config file name not set correctly :'"//global_settings%config_file_name//"'") - return - end if - end subroutine + if (global_settings%config_file_name /= 'config.toml') then + call test_failed(error, "Config file name not set correctly :'"//global_settings%config_file_name//"'") + return + end if + end subroutine - !> Config file exists and the path to that file is set. - subroutine empty_file(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + !> Config file exists and the path to that file is set. + subroutine empty_file(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings - character(:), allocatable :: cwd + character(:), allocatable :: cwd - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['']) + call filewrite(join_path(tmp_folder, config_file_name), ['']) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call get_global_settings(global_settings, error) + call get_global_settings(global_settings, error) - call delete_tmp_folder + call delete_tmp_folder - if (allocated(error)) return + if (allocated(error)) return - call get_current_directory(cwd, error) - if (allocated(error)) return + call get_current_directory(cwd, error) + if (allocated(error)) return - if (global_settings%path_to_config_folder /= join_path(cwd, tmp_folder)) then - call test_failed(error, "global_settings%path_to_config_folder not set correctly :'" & - & //global_settings%path_to_config_folder//"'"); return - end if + if (global_settings%path_to_config_folder /= join_path(cwd, tmp_folder)) then + call test_failed(error, "global_settings%path_to_config_folder not set correctly :'" & + & //global_settings%path_to_config_folder//"'"); return + end if - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'global_settings%registry_settings not be allocated'); return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'global_settings%registry_settings not be allocated'); return + end if - if (global_settings%registry_settings%url /= official_registry_base_url) then - call test_failed(error, 'Wrong default url'); return - end if + if (global_settings%registry_settings%url /= official_registry_base_url) then + call test_failed(error, 'Wrong default url'); return + end if - if (global_settings%registry_settings%cache_path /= join_path(global_settings%path_to_config_folder, & - & 'dependencies')) then - call test_failed(error, 'Wrong default cache_path'); return - end if - end subroutine + if (global_settings%registry_settings%cache_path /= join_path(global_settings%path_to_config_folder, & + & 'dependencies')) then + call test_failed(error, 'Wrong default cache_path'); return + end if + end subroutine - !> Invalid TOML file. - subroutine error_reading_table(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings + !> Invalid TOML file. + subroutine error_reading_table(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call filewrite(join_path(tmp_folder, config_file_name), ['[']) + call filewrite(join_path(tmp_folder, config_file_name), ['[']) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call get_global_settings(global_settings, error) + call get_global_settings(global_settings, error) - call delete_tmp_folder + call delete_tmp_folder - if (allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings should not be allocated'); return - end if - end subroutine + if (allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings should not be allocated'); return + end if + end subroutine - subroutine empty_registry_table(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine empty_registry_table(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call new_table(table) - call add_table(table, 'registry', child) + call new_table(table) + call add_table(table, 'registry', child) - call get_registry_settings(child, global_settings, error) - if (allocated(error)) return + call get_registry_settings(child, global_settings, error) + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated'); return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated'); return + end if - if (allocated(global_settings%registry_settings%path)) then - call test_failed(error, "Path shouldn't be allocated"); return - end if + if (allocated(global_settings%registry_settings%path)) then + call test_failed(error, "Path shouldn't be allocated"); return + end if - if (global_settings%registry_settings%url /= official_registry_base_url) then - call test_failed(error, "Url not be allocated"); return - end if - end subroutine + if (global_settings%registry_settings%url /= official_registry_base_url) then + call test_failed(error, "Url not be allocated"); return + end if + end subroutine - subroutine invalid_key(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine invalid_key(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'invalid_key', 'abc') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'invalid_key', 'abc') - call get_registry_settings(child, global_settings, error) - end subroutine + call get_registry_settings(child, global_settings, error) + end subroutine - subroutine invalid_type(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine invalid_type(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 42) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 42) - call get_registry_settings(child, global_settings, error) - end subroutine + call get_registry_settings(child, global_settings, error) + end subroutine - subroutine has_non_existent_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine has_non_existent_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'nonexistent_path') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'nonexistent_path') - call get_registry_settings(child, global_settings, error) - end subroutine + call get_registry_settings(child, global_settings, error) + end subroutine - subroutine has_existent_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child - character(:), allocatable :: cwd + subroutine has_existent_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child + character(:), allocatable :: cwd - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', '.') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', '.') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - if (.not. allocated(global_settings%registry_settings%path)) then - call test_failed(error, 'Path not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if - call delete_tmp_folder + call delete_tmp_folder - call get_current_directory(cwd, error) - if (allocated(error)) return + call get_current_directory(cwd, error) + if (allocated(error)) return - if (global_settings%registry_settings%path /= join_path(cwd, tmp_folder)) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if + if (global_settings%registry_settings%path /= join_path(cwd, tmp_folder)) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if - if (allocated(global_settings%registry_settings%url)) then - call test_failed(error, "Url shouldn't be allocated") - return - end if + if (allocated(global_settings%registry_settings%url)) then + call test_failed(error, "Url shouldn't be allocated") + return + end if - end subroutine + end subroutine - subroutine absolute_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: abs_path - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine absolute_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call get_absolute_path(tmp_folder, abs_path, error) - if (allocated(error)) return + call get_absolute_path(tmp_folder, abs_path, error) + if (allocated(error)) return - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', abs_path) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', abs_path) - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - call delete_tmp_folder + call delete_tmp_folder - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if - if (.not. allocated(global_settings%registry_settings%path)) then - call test_failed(error, 'Path not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings%path)) then + call test_failed(error, 'Path not allocated') + return + end if - if (global_settings%registry_settings%path /= abs_path) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - subroutine relative_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: abs_path - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine relative_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'abc')) + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'abc')) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', 'abc') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'abc') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - call get_absolute_path(tmp_folder, abs_path, error) + call get_absolute_path(tmp_folder, abs_path, error) - call delete_tmp_folder + call delete_tmp_folder - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if - if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - ! Test that the registry path is set correctly when the path is written to and read from a config file. - subroutine relative_path_to_registry_file_read(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: abs_path + ! Test that the registry path is set correctly when the path is written to and read from a config file. + subroutine relative_path_to_registry_file_read(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'abc')) + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'abc')) - call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) + call filewrite(join_path(tmp_folder, config_file_name), ['[registry]', 'path="abc"']) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call get_global_settings(global_settings, error) + call get_global_settings(global_settings, error) - call get_absolute_path(tmp_folder, abs_path, error) + call get_absolute_path(tmp_folder, abs_path, error) - call delete_tmp_folder + call delete_tmp_folder - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if - if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= join_path(abs_path, 'abc')) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - subroutine canonical_path_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: abs_path - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine canonical_path_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', join_path('..', tmp_folder)) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', join_path('..', tmp_folder)) - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - call get_absolute_path(tmp_folder, abs_path, error) + call get_absolute_path(tmp_folder, abs_path, error) - call delete_tmp_folder + call delete_tmp_folder - if (allocated(error)) return + if (allocated(error)) return - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if - if (global_settings%registry_settings%path /= abs_path) then - call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") - return - end if - end subroutine + if (global_settings%registry_settings%path /= abs_path) then + call test_failed(error, "Path not set correctly: '"//global_settings%registry_settings%path//"'") + return + end if + end subroutine - subroutine has_url_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine has_url_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'url', 'http') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'url', 'http') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - call delete_tmp_folder + call delete_tmp_folder - if (.not. allocated(global_settings%registry_settings)) then - call test_failed(error, 'Registry settings not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings)) then + call test_failed(error, 'Registry settings not allocated') + return + end if - if (allocated(global_settings%registry_settings%path)) then - call test_failed(error, "Path shouldn't be allocated: '" & - & //global_settings%registry_settings%path//"'") - return - end if + if (allocated(global_settings%registry_settings%path)) then + call test_failed(error, "Path shouldn't be allocated: '" & + & //global_settings%registry_settings%path//"'") + return + end if - if (.not. allocated(global_settings%registry_settings%url)) then - call test_failed(error, 'Url not allocated') - return - end if + if (.not. allocated(global_settings%registry_settings%url)) then + call test_failed(error, 'Url not allocated') + return + end if - if (global_settings%registry_settings%url /= 'http') then - call test_failed(error, 'Failed to parse url') - return - end if - end subroutine + if (global_settings%registry_settings%url /= 'http') then + call test_failed(error, 'Failed to parse url') + return + end if + end subroutine - subroutine has_both_path_and_url_to_registry(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine has_both_path_and_url_to_registry(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', '.') - call set_value(child, 'url', 'http') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', '.') + call set_value(child, 'url', 'http') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - call delete_tmp_folder - end subroutine + call delete_tmp_folder + end subroutine - subroutine has_both_path_and_cache_path(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - type(toml_table) :: table - type(toml_table), pointer :: child + subroutine has_both_path_and_cache_path(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'path', '.') - call set_value(child, 'cache_path', 'cache') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', '.') + call set_value(child, 'cache_path', 'cache') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - call delete_tmp_folder - end subroutine + call delete_tmp_folder + end subroutine - ! Custom cache location defined via absolute path but directory doesn't exist. Create it. - subroutine abs_cache_path_no_dir(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: abs_path, abs_path_to_cache - type(toml_table) :: table - type(toml_table), pointer :: child + ! Custom cache location defined via absolute path but directory doesn't exist. Create it. + subroutine abs_cache_path_no_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path, abs_path_to_cache + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call get_absolute_path(tmp_folder, abs_path, error) - if (allocated(error)) return + call get_absolute_path(tmp_folder, abs_path, error) + if (allocated(error)) return - abs_path_to_cache = join_path(abs_path, 'cache') + abs_path_to_cache = join_path(abs_path, 'cache') - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'cache_path', abs_path_to_cache) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', abs_path_to_cache) - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - if (.not. exists(abs_path_to_cache)) then - call test_failed(error, "Cache directory '"//abs_path_to_cache//"' not created.") - return - end if + if (.not. exists(abs_path_to_cache)) then + call test_failed(error, "Cache directory '"//abs_path_to_cache//"' not created.") + return + end if - if (global_settings%registry_settings%cache_path /= abs_path_to_cache) then - call test_failed(error, "Cache path '"//abs_path_to_cache//"' not registered.") - return - end if + if (global_settings%registry_settings%cache_path /= abs_path_to_cache) then + call test_failed(error, "Cache path '"//abs_path_to_cache//"' not registered.") + return + end if - call delete_tmp_folder - end subroutine + call delete_tmp_folder + end subroutine - ! Custom cache location defined via absolute path for existing directory. - subroutine abs_cache_path_has_dir(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: abs_path - type(toml_table) :: table - type(toml_table), pointer :: child + ! Custom cache location defined via absolute path for existing directory. + subroutine abs_cache_path_has_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: abs_path + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'cache')) + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache')) - call get_absolute_path(join_path(tmp_folder, 'cache'), abs_path, error) - if (allocated(error)) return + call get_absolute_path(join_path(tmp_folder, 'cache'), abs_path, error) + if (allocated(error)) return - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'cache_path', abs_path) + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', abs_path) - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - if (.not. exists(abs_path)) then - call test_failed(error, "Cache directory '"//abs_path//"' not created.") - return - end if + if (.not. exists(abs_path)) then + call test_failed(error, "Cache directory '"//abs_path//"' not created.") + return + end if - if (global_settings%registry_settings%cache_path /= abs_path) then - call test_failed(error, "Cache path '"//abs_path//"' not registered.") - return - end if + if (global_settings%registry_settings%cache_path /= abs_path) then + call test_failed(error, "Cache path '"//abs_path//"' not registered.") + return + end if - call delete_tmp_folder - end subroutine + call delete_tmp_folder + end subroutine - ! Custom cache location defined via relative path but directory doesn't exist. Create it. - subroutine rel_cache_path_no_dir(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(:), allocatable :: cache_path, abs_path - type(toml_table) :: table - type(toml_table), pointer :: child + ! Custom cache location defined via relative path but directory doesn't exist. Create it. + subroutine rel_cache_path_no_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(:), allocatable :: cache_path, abs_path + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder - call mkdir(tmp_folder) + call delete_tmp_folder + call mkdir(tmp_folder) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'cache_path', 'cache') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', 'cache') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - cache_path = join_path(tmp_folder, 'cache') + cache_path = join_path(tmp_folder, 'cache') - if (.not. exists(cache_path)) then - call test_failed(error, "Cache directory '"//cache_path//"' not created.") - return - end if + if (.not. exists(cache_path)) then + call test_failed(error, "Cache directory '"//cache_path//"' not created.") + return + end if - call get_absolute_path(cache_path, abs_path, error) - if (allocated(error)) return + call get_absolute_path(cache_path, abs_path, error) + if (allocated(error)) return - if (global_settings%registry_settings%cache_path /= abs_path) then - call test_failed(error, "Cache path '"//cache_path//"' not registered.") - return - end if + if (global_settings%registry_settings%cache_path /= abs_path) then + call test_failed(error, "Cache path '"//cache_path//"' not registered.") + return + end if - call delete_tmp_folder - end subroutine + call delete_tmp_folder + end subroutine - ! Custom cache location defined via relative path for existing directory. - subroutine rel_cache_path_has_dir(error) - type(error_t), allocatable, intent(out) :: error - type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: cache_path, abs_path - type(toml_table) :: table - type(toml_table), pointer :: child + ! Custom cache location defined via relative path for existing directory. + subroutine rel_cache_path_has_dir(error) + type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: cache_path, abs_path + type(toml_table) :: table + type(toml_table), pointer :: child - call delete_tmp_folder + call delete_tmp_folder - cache_path = join_path(tmp_folder, 'cache') - call mkdir(cache_path) + cache_path = join_path(tmp_folder, 'cache') + call mkdir(cache_path) - call setup_global_settings(global_settings, error) - if (allocated(error)) return + call setup_global_settings(global_settings, error) + if (allocated(error)) return - call new_table(table) - call add_table(table, 'registry', child) - call set_value(child, 'cache_path', 'cache') + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'cache_path', 'cache') - call get_registry_settings(child, global_settings, error) + call get_registry_settings(child, global_settings, error) - if (.not. exists(cache_path)) then - call test_failed(error, "Cache directory '"//cache_path//"' not created.") - return - end if + if (.not. exists(cache_path)) then + call test_failed(error, "Cache directory '"//cache_path//"' not created.") + return + end if - call get_absolute_path(cache_path, abs_path, error) - if (allocated(error)) return + call get_absolute_path(cache_path, abs_path, error) + if (allocated(error)) return - if (global_settings%registry_settings%cache_path /= abs_path) then - call test_failed(error, "Cache path '"//cache_path//"' not registered.") - return - end if + if (global_settings%registry_settings%cache_path /= abs_path) then + call test_failed(error, "Cache path '"//cache_path//"' not registered.") + return + end if - call delete_tmp_folder - end subroutine + call delete_tmp_folder + end subroutine end module test_settings From a23a1e59044afa238eca61f6805ea16f75dd72d4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 4 Mar 2023 16:25:56 +0100 Subject: [PATCH 126/799] Move assignment to subroutine --- src/fpm/dependency.f90 | 14 ++++---------- src/fpm/downloader.f90 | 17 +++++++++++++---- test/fpm_test/test_package_dependencies.f90 | 9 +++++++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index abc1b2c388..8fff96191d 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -68,7 +68,7 @@ module fpm_dependency use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_downloader, only: downloader_t - use jonquil, only: json_object, json_value, cast_to_object + use jonquil, only: json_object implicit none private @@ -516,8 +516,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, downloaded_version, code_str type(version_t) :: version integer :: stat, unit, code - class(json_value), allocatable :: j_value - type(json_object), pointer :: json + type(json_object) :: json ! Use local registry if it was specified in the global config file. if (allocated(global_settings%registry_settings%path)) then @@ -551,20 +550,15 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (allocated(self%requested_version)) then ! Request specific version. - call downloader%get_pkg_data(target_url//'/'//self%requested_version%s(), tmp_file, j_value, error) + call downloader%get_pkg_data(target_url//'/'//self%requested_version%s(), tmp_file, json, error) else ! Request latest version. - call downloader%get_pkg_data(target_url, tmp_file, j_value, error) + call downloader%get_pkg_data(target_url, tmp_file, json, error) end if close (unit, status='delete') if (allocated(error)) return - json => cast_to_object(j_value) - if (.not. associated(json)) then - call fatal_error(error, "Error parsing JSON from '"//target_url//"'."); return - end if - if (.not. json%has_key('code')) then call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No status code."); return end if diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 0c12c6e6bc..bed6b0d344 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,7 +1,7 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error use fpm_filesystem, only: which - use jonquil, only: json_value, json_error, json_load + use jonquil, only: json_object, json_value, json_error, json_load, cast_to_object implicit none private @@ -20,18 +20,27 @@ module fpm_downloader subroutine get_pkg_data(url, tmp_file, json, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_file - class(json_value), allocatable, intent(out) :: json + type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error - class(json_error), allocatable :: j_error + class(json_value), allocatable :: raw + type(json_object), pointer :: ptr + type(json_error), allocatable :: j_error call get_file(url, tmp_file, error) if (allocated(error)) return - call json_load(json, tmp_file, error=j_error) + call json_load(raw, tmp_file, error=j_error) if (allocated(j_error)) then allocate (error); call move_alloc(j_error%message, error%message); call json%destroy(); return end if + + ptr => cast_to_object(raw) + if (.not. associated(ptr)) then + call fatal_error(error, "Error parsing JSON from '"//url//"'."); return + end if + + json = ptr end subroutine get_file(url, tmp_file, error) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index d8696d9542..f8a3cbc0db 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -747,10 +747,15 @@ subroutine setup_global_settings(global_settings, error) subroutine get_pkg_data(url, tmp_file, json, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_file - class(json_value), allocatable, intent(out) :: json + type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error - call json_loads(json, '{"code": 200, "version": "0.0.1", "tar": "abc"}') + class(json_value), allocatable :: raw + type(json_object), pointer :: ptr + + call json_loads(raw, '{"code": 200, "version": "0.0.1", "tar": "abc"}') + ptr => cast_to_object(raw) + json = ptr end subroutine get_file(url, tmp_file, error) From 762d55961fc81d79dc552e662155642533e37759 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 4 Mar 2023 16:45:20 +0100 Subject: [PATCH 127/799] Extract check_package_data --- src/fpm/dependency.f90 | 61 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 8fff96191d..c584331854 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -69,6 +69,7 @@ module fpm_dependency use fpm_settings, only: fpm_global_settings, get_global_settings use fpm_downloader, only: downloader_t use jonquil, only: json_object + use fpm_strings, only: str implicit none private @@ -513,9 +514,9 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade !> Downloader instance. class(downloader_t), optional, intent(in) :: downloader - character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, downloaded_version, code_str + character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, downloaded_version type(version_t) :: version - integer :: stat, unit, code + integer :: stat, unit type(json_object) :: json ! Use local registry if it was specified in the global config file. @@ -543,7 +544,6 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade tmp_file = join_path(tmp_path, 'package_data.tmp') if (.not. exists(tmp_path)) call mkdir(tmp_path) open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) - if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if @@ -559,57 +559,34 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade close (unit, status='delete') if (allocated(error)) return - if (.not. json%has_key('code')) then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No status code."); return - end if - - call get_value(json, 'code', code, stat=stat) - - if (code /= 200 .or. stat /= 0) then - allocate (character(int(log10(real(code))) + 1) :: code_str) - write (code_str, '(I0)') code - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': " & - & //"Status code '"//code_str//"'."); return - end if - - if (.not. json%has_key('tar')) then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No download link."); return - end if + call check_package_data(json, self, error) + if (allocated(error)) return ! Get download link and version of the package. call get_value(json, 'tar', target_url, stat=stat) - if (stat /= 0) then call fatal_error(error, "Failed to get download link from '"//join_path(self%namespace, self%name)//"'."); return end if - if (.not. json%has_key('version')) then - call fatal_error(error, "Failed to download '"//join_path(self%namespace, self%name)//"': No version found."); return - end if - ! Get version of the package. call get_value(json, 'version', downloaded_version, stat=stat) - if (stat /= 0) then call fatal_error(error, "Failed to download version from '"//join_path(self%namespace, self%name)//"'."); return end if call new_version(version, downloaded_version, error) - if (allocated(error)) then call fatal_error(error, "Version not valid: '"//downloaded_version//"'."); return end if ! Open new tmp file for downloading the actual package. open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) - if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." call downloader%get_file(target_url, tmp_file, error) - if (allocated(error)) then close (unit, status='delete'); return end if @@ -620,15 +597,39 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade ! Unpack the downloaded package to the final location. call downloader%unpack(tmp_file, cache_path, error) - close (unit, status='delete') - if (allocated(error)) return target_dir = cache_path end subroutine get_from_registry + subroutine check_package_data(json, node, error) + type(json_object), intent(inout) :: json + class(dependency_node_t), intent(in) :: node + type(error_t), allocatable, intent(out) :: error + + integer :: code, stat + + if (.not. json%has_key('code')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No status code."); return + end if + + if (.not. json%has_key('tar')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No download link."); return + end if + + if (.not. json%has_key('version')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version found."); return + end if + + call get_value(json, 'code', code, stat=stat) + if (code /= 200 .or. stat /= 0) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': " & + & //"Status code '"//str(code)//"'."); return + end if + end subroutine + !> Get the dependency from a local registry. subroutine get_from_local_registry(self, target_dir, registry_path, error) From d0c7d918b263202ff32dc141464c042348bdbcac Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 4 Mar 2023 16:53:33 +0100 Subject: [PATCH 128/799] Call it j_value again --- src/fpm/downloader.f90 | 6 +++--- test/fpm_test/test_package_dependencies.f90 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index bed6b0d344..f87baf73ca 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -23,19 +23,19 @@ subroutine get_pkg_data(url, tmp_file, json, error) type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error - class(json_value), allocatable :: raw + class(json_value), allocatable :: j_value type(json_object), pointer :: ptr type(json_error), allocatable :: j_error call get_file(url, tmp_file, error) if (allocated(error)) return - call json_load(raw, tmp_file, error=j_error) + call json_load(j_value, tmp_file, error=j_error) if (allocated(j_error)) then allocate (error); call move_alloc(j_error%message, error%message); call json%destroy(); return end if - ptr => cast_to_object(raw) + ptr => cast_to_object(j_value) if (.not. associated(ptr)) then call fatal_error(error, "Error parsing JSON from '"//url//"'."); return end if diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index f8a3cbc0db..ba6926b962 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -750,11 +750,11 @@ subroutine get_pkg_data(url, tmp_file, json, error) type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error - class(json_value), allocatable :: raw + class(json_value), allocatable :: j_value type(json_object), pointer :: ptr - call json_loads(raw, '{"code": 200, "version": "0.0.1", "tar": "abc"}') - ptr => cast_to_object(raw) + call json_loads(j_value, '{"code": 200, "version": "0.0.1", "tar": "abc"}') + ptr => cast_to_object(j_value) json = ptr end From f2b4df1afe45a3b229e1e6e96ae56686e5b250ec Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 5 Mar 2023 16:13:21 +0100 Subject: [PATCH 129/799] Extract reading of pkg data, too --- src/fpm/dependency.f90 | 51 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index c584331854..02e331457b 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -66,7 +66,7 @@ module fpm_dependency use fpm_toml, only: toml_table, toml_key, toml_error, toml_serializer, & toml_parse, get_value, set_value, add_table, toml_load, toml_stat use fpm_versioning, only: version_t, new_version - use fpm_settings, only: fpm_global_settings, get_global_settings + use fpm_settings, only: fpm_global_settings, get_global_settings, official_registry_base_url use fpm_downloader, only: downloader_t use jonquil, only: json_object use fpm_strings, only: str @@ -514,7 +514,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade !> Downloader instance. class(downloader_t), optional, intent(in) :: downloader - character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path, downloaded_version + character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path type(version_t) :: version integer :: stat, unit type(json_object) :: json @@ -559,26 +559,9 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade close (unit, status='delete') if (allocated(error)) return - call check_package_data(json, self, error) + call check_and_read_pkg_data(json, self, target_url, version, error) if (allocated(error)) return - ! Get download link and version of the package. - call get_value(json, 'tar', target_url, stat=stat) - if (stat /= 0) then - call fatal_error(error, "Failed to get download link from '"//join_path(self%namespace, self%name)//"'."); return - end if - - ! Get version of the package. - call get_value(json, 'version', downloaded_version, stat=stat) - if (stat /= 0) then - call fatal_error(error, "Failed to download version from '"//join_path(self%namespace, self%name)//"'."); return - end if - - call new_version(version, downloaded_version, error) - if (allocated(error)) then - call fatal_error(error, "Version not valid: '"//downloaded_version//"'."); return - end if - ! Open new tmp file for downloading the actual package. open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) if (stat /= 0) then @@ -604,12 +587,15 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end subroutine get_from_registry - subroutine check_package_data(json, node, error) + subroutine check_and_read_pkg_data(json, node, download_link, version, error) type(json_object), intent(inout) :: json class(dependency_node_t), intent(in) :: node + character(:), allocatable, intent(out) :: download_link + type(version_t), intent(out) :: version type(error_t), allocatable, intent(out) :: error integer :: code, stat + character(:), allocatable :: version_str if (.not. json%has_key('code')) then call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No status code."); return @@ -625,8 +611,27 @@ subroutine check_package_data(json, node, error) call get_value(json, 'code', code, stat=stat) if (code /= 200 .or. stat /= 0) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': " & - & //"Status code '"//str(code)//"'."); return + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "//"Status code '"// & + & str(code)//"'."); return + end if + + ! Get download link and version of the package. + call get_value(json, 'tar', download_link, stat=stat) + if (stat /= 0) then + call fatal_error(error, "Failed to get download link from '"//join_path(node%namespace, node%name)//"'."); return + end if + + download_link = official_registry_base_url//download_link + + ! Get version of the package. + call get_value(json, 'version', version_str, stat=stat) + if (stat /= 0) then + call fatal_error(error, "Failed to download version from '"//join_path(node%namespace, node%name)//"'."); return + end if + + call new_version(version, version_str, error) + if (allocated(error)) then + call fatal_error(error, "Invalid version: '"//version_str//"'."); return end if end subroutine From d8558c0d411e38825658b7358901bebeac2334e7 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 5 Mar 2023 17:37:45 +0100 Subject: [PATCH 130/799] Adapt to new JSON response --- src/fpm/dependency.f90 | 72 +++++++++++++-------- src/fpm/downloader.f90 | 12 +++- test/fpm_test/test_package_dependencies.f90 | 15 +++-- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 02e331457b..a386f89f8f 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -536,9 +536,6 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end if end if - ! Include namespace and package name in the target url. - target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name - ! Define location of the temporary folder and file. tmp_path = join_path(global_settings%path_to_config_folder, 'tmp') tmp_file = join_path(tmp_path, 'package_data.tmp') @@ -548,13 +545,9 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if - if (allocated(self%requested_version)) then - ! Request specific version. - call downloader%get_pkg_data(target_url//'/'//self%requested_version%s(), tmp_file, json, error) - else - ! Request latest version. - call downloader%get_pkg_data(target_url, tmp_file, json, error) - end if + ! Include namespace and package name in the target url and download package data. + target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name + call downloader%get_pkg_data(target_url, self%requested_version, tmp_file, json, error) close (unit, status='delete') if (allocated(error)) return @@ -587,46 +580,69 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end subroutine get_from_registry - subroutine check_and_read_pkg_data(json, node, download_link, version, error) + subroutine check_and_read_pkg_data(json, node, download_url, version, error) type(json_object), intent(inout) :: json class(dependency_node_t), intent(in) :: node - character(:), allocatable, intent(out) :: download_link + character(:), allocatable, intent(out) :: download_url type(version_t), intent(out) :: version type(error_t), allocatable, intent(out) :: error integer :: code, stat - character(:), allocatable :: version_str + type(json_object), pointer :: p, q + character(:), allocatable :: version_key, version_str if (.not. json%has_key('code')) then call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No status code."); return end if - if (.not. json%has_key('tar')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No download link."); return - end if - - if (.not. json%has_key('version')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version found."); return - end if - call get_value(json, 'code', code, stat=stat) if (code /= 200 .or. stat /= 0) then call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "//"Status code '"// & & str(code)//"'."); return end if - ! Get download link and version of the package. - call get_value(json, 'tar', download_link, stat=stat) + if (.not. json%has_key('data')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No data."); return + end if + + call get_value(json, 'data', p, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to get download link from '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to retrieve package data for '"//join_path(node%namespace, node%name)//"'."); return end if - download_link = official_registry_base_url//download_link + if (allocated(node%requested_version)) then + version_key = 'version_data' + else + version_key = 'latest_version_data' + end if + + if (.not. p%has_key(version_key)) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version data."); return + end if + + call get_value(p, version_key, q, stat=stat) + if (stat /= 0) then + call fatal_error(error, "Failed to retrieve version data for '"//join_path(node%namespace, node%name)//"'."); return + end if + + if (.not. q%has_key('download_url')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No download url."); return + end if + + call get_value(q, 'download_url', download_url, stat=stat) + if (stat /= 0) then + call fatal_error(error, "Failed to retrieve download url for '"//join_path(node%namespace, node%name)//"'."); return + end if + + download_url = official_registry_base_url//download_url + + if (.not. q%has_key('version')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version found."); return + end if - ! Get version of the package. - call get_value(json, 'version', version_str, stat=stat) + call get_value(q, 'version', version_str, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to download version from '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to retrieve version data for '"//join_path(node%namespace, node%name)//"'."); return end if call new_version(version, version_str, error) diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index f87baf73ca..da64dda985 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,6 +1,7 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error use fpm_filesystem, only: which + use fpm_versioning, only: version_t use jonquil, only: json_object, json_value, json_error, json_load, cast_to_object implicit none @@ -17,8 +18,9 @@ module fpm_downloader contains !> Perform an http get request and save output to file. - subroutine get_pkg_data(url, tmp_file, json, error) + subroutine get_pkg_data(url, version, tmp_file, json, error) character(*), intent(in) :: url + type(version_t), allocatable, intent(in) :: version character(*), intent(in) :: tmp_file type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error @@ -27,7 +29,13 @@ subroutine get_pkg_data(url, tmp_file, json, error) type(json_object), pointer :: ptr type(json_error), allocatable :: j_error - call get_file(url, tmp_file, error) + if (allocated(version)) then + ! Request specific version. + call get_file(url//'/'//version%s(), tmp_file, error) + else + ! Request latest version. + call get_file(url, tmp_file, error) + end if if (allocated(error)) return call json_load(j_value, tmp_file, error=j_error) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index ba6926b962..b2395a28ee 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -10,6 +10,7 @@ module test_package_dependencies use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings use fpm_downloader, only: downloader_t + use fpm_versioning, only: version_t use jonquil, only: json_object, json_value, json_loads, cast_to_object implicit none @@ -744,18 +745,22 @@ subroutine setup_global_settings(global_settings, error) global_settings%config_file_name = config_file_name end - subroutine get_pkg_data(url, tmp_file, json, error) + subroutine get_pkg_data(url, version, tmp_file, json, error) character(*), intent(in) :: url + type(version_t), allocatable, intent(in) :: version character(*), intent(in) :: tmp_file type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error class(json_value), allocatable :: j_value - type(json_object), pointer :: ptr - call json_loads(j_value, '{"code": 200, "version": "0.0.1", "tar": "abc"}') - ptr => cast_to_object(j_value) - json = ptr + if (allocated(version)) then + call json_loads(j_value, '{"code": 200, "data": {"version_data": {"version": "0.1.0", "download_url": "abc"}}}') + else + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {"version": "0.1.0", "download_url": "abc"}}}') + end if + + json = cast_to_object(j_value) end subroutine get_file(url, tmp_file, error) From 6cdd58b5616c715fd6a0b4417df0d06258701d9f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 6 Mar 2023 20:34:15 +0100 Subject: [PATCH 131/799] Only download packge if not existent, add comments --- src/fpm/dependency.f90 | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index a386f89f8f..91dc7abf63 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -527,7 +527,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade ! Include namespace and package name in the cache path. cache_path = join_path(global_settings%registry_settings%cache_path, self%namespace, self%name) - ! Check cache before downloading from the remote registry if a specific version was requested. + ! Check cache before downloading from the remote registry if a specific version was requested. When no specific + ! version was requested, do network request first to check which is the newest version. if (allocated(self%requested_version)) then cache_path = join_path(cache_path, self%requested_version%s()) if (exists(join_path(cache_path, 'fpm.toml'))) then @@ -548,10 +549,10 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade ! Include namespace and package name in the target url and download package data. target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name call downloader%get_pkg_data(target_url, self%requested_version, tmp_file, json, error) - close (unit, status='delete') if (allocated(error)) return + ! Verify package data read relevant information. call check_and_read_pkg_data(json, self, target_url, version, error) if (allocated(error)) return @@ -561,20 +562,22 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if - print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." - call downloader%get_file(target_url, tmp_file, error) - if (allocated(error)) then - close (unit, status='delete'); return - end if - - ! Include version number in the cache path. + ! Include version number in the cache path. In no cached version exists, download it. cache_path = join_path(cache_path, version%s()) - if (.not. exists(cache_path)) call mkdir(cache_path) + if (.not. exists(join_path(cache_path, 'fpm.toml'))) then + if (.not. exists(cache_path)) call mkdir(cache_path) - ! Unpack the downloaded package to the final location. - call downloader%unpack(tmp_file, cache_path, error) - close (unit, status='delete') - if (allocated(error)) return + print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." + call downloader%get_file(target_url, tmp_file, error) + if (allocated(error)) then + close (unit, status='delete'); return + end if + + ! Unpack the downloaded package to the final location. + call downloader%unpack(tmp_file, cache_path, error) + close (unit, status='delete') + if (allocated(error)) return + end if target_dir = cache_path From a66990892bca473f09aaa4039d386eed4dcea320 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 01:08:38 +0100 Subject: [PATCH 132/799] Check manifests in local registry, add tests --- src/fpm/dependency.f90 | 13 +- test/fpm_test/test_package_dependencies.f90 | 159 +++++++++++++++++++- 2 files changed, 164 insertions(+), 8 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 91dc7abf63..48131704ab 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -692,6 +692,9 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) do i = 1, size(files) ! Identify directory that matches the version number. if (files(i)%s == join_path(path_to_name, self%requested_version%s()) .and. is_dir(files(i)%s)) then + if (.not. exists(join_path(files(i)%s, 'fpm.toml'))) then + call fatal_error(error, "'"//files(i)%s//"' is missing an 'fpm.toml' file."); return + end if target_dir = files(i)%s; return end if end do @@ -699,7 +702,7 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) return end if - ! No version requested, generate list of available versions. + ! No specific version requested, therefore collect available versions. allocate (versions(0)) do i = 1, size(files) if (is_dir(files(i)%s)) then @@ -719,7 +722,13 @@ subroutine get_from_local_registry(self, target_dir, registry_path, error) if (versions(i) > version) version = versions(i) end do - target_dir = join_path(path_to_name, version%s()) + path_to_name = join_path(path_to_name, version%s()) + + if (.not. exists(join_path(path_to_name, 'fpm.toml'))) then + call fatal_error(error, "'"//path_to_name//"' is missing an 'fpm.toml' file."); return + end if + + target_dir = path_to_name end subroutine get_from_local_registry !> True if dependency is part of the tree diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index b2395a28ee..4fd3dcfbae 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -47,9 +47,12 @@ subroutine collect_package_dependencies(tests) & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & & new_unittest("version-not-found-in-registry", version_not_found_in_registry, should_fail=.true.), & - & new_unittest("found-in-registry", version_found_in_registry), & + & new_unittest("version-found-without-manifest", version_found_without_manifest, should_fail=.true.), & + & new_unittest("version-found-with-manifest", version_found_with_manifest), & & new_unittest("not-a-dir", not_a_dir, should_fail=.true.), & - & new_unittest("newest-version-in-registry", newest_version_in_registry), & + & new_unittest("no-versions-found", no_versions_found, should_fail=.true.), & + & new_unittest("newest-version-without-manifest", newest_version_without_manifest, should_fail=.true.), & + & new_unittest("newest-version-with-manifest", newest_version_with_manifest), & & new_unittest("default-cache-path", default_cache_path), & & new_unittest("version-found-in-cache", version_found_in_cache), & & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & @@ -361,7 +364,52 @@ subroutine version_not_found_in_registry(error) end subroutine version_not_found_in_registry - subroutine version_found_in_registry(error) + subroutine version_found_without_manifest(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '0.1.0') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.9')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.1')) + + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call delete_tmp_folder + + end subroutine version_found_without_manifest + + subroutine version_found_with_manifest(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -381,6 +429,7 @@ subroutine version_found_in_registry(error) call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) + call filewrite(join_path(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'), 'fpm.toml'), ['']) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.2.0')) call new_table(table) @@ -414,7 +463,7 @@ subroutine version_found_in_registry(error) call delete_tmp_folder - end subroutine version_found_in_registry + end subroutine version_found_with_manifest subroutine not_a_dir(error) type(error_t), allocatable, intent(out) :: error @@ -459,7 +508,104 @@ subroutine not_a_dir(error) end subroutine not_a_dir - subroutine newest_version_in_registry(error) + ! Compared to no-versions-in-registry, we aren't requesting a specific version here. + subroutine no_versions_found(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep')) + + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call delete_tmp_folder + + end subroutine no_versions_found + + subroutine newest_version_without_manifest(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) + + call new_table(table) + call add_table(table, 'registry', child) + call set_value(child, 'path', 'cache') + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'))) then + call test_failed(error, 'target_dir not set correctly: '//target_dir//"'") + call delete_tmp_folder; return + end if + + call delete_tmp_folder + + end subroutine newest_version_without_manifest + + subroutine newest_version_with_manifest(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -478,6 +624,7 @@ subroutine newest_version_in_registry(error) call delete_tmp_folder call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0')) + call filewrite(join_path(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.3.0'), 'fpm.toml'), ['']) call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) call new_table(table) @@ -511,7 +658,7 @@ subroutine newest_version_in_registry(error) call delete_tmp_folder - end subroutine newest_version_in_registry + end subroutine newest_version_with_manifest !> No cache_path specified, use default cache path but folder exists already. subroutine default_cache_path(error) From 80d7f3f01453ca0996784cb46cb4beb82f3bcb31 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 8 Mar 2023 16:14:15 +0100 Subject: [PATCH 133/799] return program's exit code --- src/fpm.f90 | 8 +++++--- src/fpm_filesystem.F90 | 6 ++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index a7ff6fa6b1..478ed90762 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -441,7 +441,7 @@ subroutine cmd_run(settings,test) type(string_t), allocatable :: executables(:) type(build_target_t), pointer :: exe_target type(srcfile_t), pointer :: exe_source - integer :: run_scope + integer :: run_scope,firsterror integer, allocatable :: stat(:) character(len=:),allocatable :: line logical :: toomany @@ -586,10 +586,12 @@ subroutine cmd_run(settings,test) if (any(stat /= 0)) then do i=1,size(stat) if (stat(i) /= 0) then - write(stderr,'(*(g0:,1x))') ' Execution failed for object "',basename(executables(i)%s),'"' + write(stderr,'(*(g0:,1x))') ' Execution for object "',basename(executables(i)%s),& + '" returned exit code ',stat(i) end if end do - call fpm_stop(1,'*cmd_run*:stopping due to failed executions') + firsterror = findloc(stat/=0,value=.true.,dim=1) + call fpm_stop(stat(firsterror),'*cmd_run*:stopping due to failed executions') end if endif diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 0a75746ff4..36c4127089 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -924,10 +924,8 @@ subroutine run(cmd,echo,exitstat,verbose,redirect) if (present(exitstat)) then exitstat = stat - else - if (stat /= 0) then - call fpm_stop(1,'*run*:Command failed') - end if + elseif (stat /= 0) then + call fpm_stop(stat,'*run*: Command '//cmd//redirect_str//' returned a non-zero status code') end if end subroutine run From e02b917b6f953a98918b7806695fa42f0457e324 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 16:20:40 +0100 Subject: [PATCH 134/799] Use default registry settings if config file does not exist --- src/fpm_settings.f90 | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index f3b5e9421d..927b83f6df 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -79,8 +79,10 @@ subroutine get_global_settings(global_settings, error) ! Use default file name. global_settings%config_file_name = 'config.toml' - ! Return if config file doesn't exist. - if (.not. exists(global_settings%full_path())) return + ! Apply default registry settings and return if config file doesn't exist. + if (.not. exists(global_settings%full_path())) then + call use_default_registry_settings(global_settings); return + end if end if ! Load into TOML table. @@ -99,17 +101,24 @@ subroutine get_global_settings(global_settings, error) ! A registry table was found. if (associated(registry_table)) then - call get_registry_settings(registry_table, global_settings, error); return + call get_registry_settings(registry_table, global_settings, error) else - ! No registry table was found, use default settings for url and cache_path. - allocate (global_settings%registry_settings) - global_settings%registry_settings%url = official_registry_base_url - global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & - & 'dependencies'); return + call use_default_registry_settings(global_settings) end if end subroutine get_global_settings + !> Default registry settings are typically applied if the config file doesn't exist or no registry table was found in + !> the global config file. + subroutine use_default_registry_settings(global_settings) + type(fpm_global_settings), intent(inout) :: global_settings + + allocate (global_settings%registry_settings) + global_settings%registry_settings%url = official_registry_base_url + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & + & 'dependencies') + end subroutine use_default_registry_settings + !> Read registry settings from the global config file. subroutine get_registry_settings(table, global_settings, error) !> The [registry] subtable from the global config file. From 5823d8e7516fc3a3529d2d5a958a8927deba29c9 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 16:22:22 +0100 Subject: [PATCH 135/799] Not include version twice, parse error message, add comments and tests --- src/fpm/dependency.f90 | 30 ++++-- test/fpm_test/test_package_dependencies.f90 | 103 ++++++++++++++++---- 2 files changed, 108 insertions(+), 25 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 48131704ab..baba22a6be 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -530,10 +530,9 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade ! Check cache before downloading from the remote registry if a specific version was requested. When no specific ! version was requested, do network request first to check which is the newest version. if (allocated(self%requested_version)) then - cache_path = join_path(cache_path, self%requested_version%s()) - if (exists(join_path(cache_path, 'fpm.toml'))) then - print *, "Using cached version of '", join_path(self%namespace, self%name, self%requested_version%s()), "'" - target_dir = cache_path; return + if (exists(join_path(cache_path, self%requested_version%s(), 'fpm.toml'))) then + print *, "Using cached version of '", join_path(self%namespace, self%name, self%requested_version%s()), "'." + target_dir = join_path(cache_path, self%requested_version%s()); return end if end if @@ -592,16 +591,31 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) integer :: code, stat type(json_object), pointer :: p, q - character(:), allocatable :: version_key, version_str + character(:), allocatable :: version_key, version_str, error_message if (.not. json%has_key('code')) then call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No status code."); return end if call get_value(json, 'code', code, stat=stat) - if (code /= 200 .or. stat /= 0) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "//"Status code '"// & - & str(code)//"'."); return + if (stat /= 0) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "// & + & "Failed to read status code."); return + end if + + if (code /= 200) then + if (.not. json%has_key('message')) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No error message."); return + end if + + call get_value(json, 'message', error_message, stat=stat) + if (stat /= 0) then + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "// & + & "Failed to read error message."); return + end if + + call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"'. Status code: '"// & + & str(code)//"'. Error message: '"//error_message//"'."); return end if if (.not. json%has_key('data')) then diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 4fd3dcfbae..76052162f9 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -53,9 +53,10 @@ subroutine collect_package_dependencies(tests) & new_unittest("no-versions-found", no_versions_found, should_fail=.true.), & & new_unittest("newest-version-without-manifest", newest_version_without_manifest, should_fail=.true.), & & new_unittest("newest-version-with-manifest", newest_version_with_manifest), & - & new_unittest("default-cache-path", default_cache_path), & + & new_unittest("get-newest-version-from-registry", get_newest_version_from_registry), & & new_unittest("version-found-in-cache", version_found_in_cache), & & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & + & new_unittest("no-version-in-cache-or-registry", no_version_in_cache_or_registry, should_fail=.true.), & & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache) & & ] @@ -236,6 +237,7 @@ subroutine test_add_dependencies(error) end subroutine test_add_dependencies + !> Directories for namespace and package name not found in path registry. subroutine registry_dir_not_found(error) type(error_t), allocatable, intent(out) :: error @@ -278,6 +280,7 @@ subroutine registry_dir_not_found(error) end subroutine registry_dir_not_found + !> No versions found in path registry. subroutine no_versions_in_registry(error) type(error_t), allocatable, intent(out) :: error @@ -320,6 +323,7 @@ subroutine no_versions_in_registry(error) end subroutine no_versions_in_registry + !> Specific version not found in path registry. subroutine version_not_found_in_registry(error) type(error_t), allocatable, intent(out) :: error @@ -364,6 +368,7 @@ subroutine version_not_found_in_registry(error) end subroutine version_not_found_in_registry + !> Target package in path registry does not contain manifest. subroutine version_found_without_manifest(error) type(error_t), allocatable, intent(out) :: error @@ -409,6 +414,7 @@ subroutine version_found_without_manifest(error) end subroutine version_found_without_manifest + !> Target package in path registry contains manifest. subroutine version_found_with_manifest(error) type(error_t), allocatable, intent(out) :: error @@ -465,6 +471,7 @@ subroutine version_found_with_manifest(error) end subroutine version_found_with_manifest + !> Target is a file, not a directory. subroutine not_a_dir(error) type(error_t), allocatable, intent(out) :: error @@ -508,7 +515,8 @@ subroutine not_a_dir(error) end subroutine not_a_dir - ! Compared to no-versions-in-registry, we aren't requesting a specific version here. + !> Try fetching the latest version in the local registry, but none are found. + !> Compared to no-versions-in-registry, we aren't requesting a specific version here. subroutine no_versions_found(error) type(error_t), allocatable, intent(out) :: error @@ -551,6 +559,7 @@ subroutine no_versions_found(error) end subroutine no_versions_found + !> Latest version in the local registry does not have a manifest. subroutine newest_version_without_manifest(error) type(error_t), allocatable, intent(out) :: error @@ -605,6 +614,7 @@ subroutine newest_version_without_manifest(error) end subroutine newest_version_without_manifest + !> Latest version in the local registry has a manifest. subroutine newest_version_with_manifest(error) type(error_t), allocatable, intent(out) :: error @@ -660,14 +670,14 @@ subroutine newest_version_with_manifest(error) end subroutine newest_version_with_manifest - !> No cache_path specified, use default cache path but folder exists already. - subroutine default_cache_path(error) + !> No version specified, get the newest version from the registry. + subroutine get_newest_version_from_registry(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir + character(len=:), allocatable :: target_dir, cwd type(toml_table), pointer :: child type(mock_downloader_t) :: mock_downloader @@ -681,14 +691,14 @@ subroutine default_cache_path(error) call delete_tmp_folder call mkdir(tmp_folder) + call new_table(table) + call add_table(table, 'registry', child) + call setup_global_settings(global_settings, error) if (allocated(error)) then call delete_tmp_folder; return end if - call new_table(table) - call add_table(table, 'registry', child) ! No cache_path specified, use default - call get_registry_settings(child, global_settings, error) if (allocated(error)) then call delete_tmp_folder; return @@ -699,21 +709,21 @@ subroutine default_cache_path(error) call delete_tmp_folder; return end if - if (global_settings%registry_settings%cache_path /= & - & join_path(global_settings%path_to_config_folder, 'dependencies')) then - call test_failed(error, 'Cache path not correctly set: '//global_settings%registry_settings%cache_path//"'") + call get_current_directory(cwd, error) + if (allocated(error)) then call delete_tmp_folder; return end if - if (.not. exists(global_settings%registry_settings%cache_path)) then - call test_failed(error, 'Folder does not exist: '//global_settings%registry_settings%cache_path//"'") + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then + call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") call delete_tmp_folder; return end if call delete_tmp_folder - end subroutine default_cache_path + end subroutine get_newest_version_from_registry + !> Version specified in manifest, version found in cache. subroutine version_found_in_cache(error) type(error_t), allocatable, intent(out) :: error @@ -737,7 +747,7 @@ subroutine version_found_in_cache(error) call filewrite(join_path(path, 'fpm.toml'), ['']) call new_table(table) - call add_table(table, 'registry', child) + call add_table(table, 'registry', child) ! No cache_path specified, use default call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -768,13 +778,14 @@ subroutine version_found_in_cache(error) end subroutine version_found_in_cache + !> Version specified in manifest, but not found in cache. Therefore download dependency. subroutine no_version_in_default_cache(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir + character(len=:), allocatable :: target_dir, cwd type(toml_table), pointer :: child type(mock_downloader_t) :: mock_downloader @@ -807,10 +818,64 @@ subroutine no_version_in_default_cache(error) call delete_tmp_folder; return end if + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then + call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") + call delete_tmp_folder; return + end if + call delete_tmp_folder end subroutine no_version_in_default_cache + !> Version specified in manifest, but not found in cache or registry. + subroutine no_version_in_cache_or_registry(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir + type(toml_table), pointer :: child + type(mock_downloader_t) :: mock_downloader + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '9.9.9') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(tmp_folder) + + call new_table(table) + call add_table(table, 'registry', child) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error, mock_downloader) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call delete_tmp_folder + + end subroutine no_version_in_cache_or_registry + subroutine other_versions_in_default_cache(error) type(error_t), allocatable, intent(out) :: error @@ -902,7 +967,11 @@ subroutine get_pkg_data(url, version, tmp_file, json, error) class(json_value), allocatable :: j_value if (allocated(version)) then - call json_loads(j_value, '{"code": 200, "data": {"version_data": {"version": "0.1.0", "download_url": "abc"}}}') + if (version%s() == '9.9.9') then + call json_loads(j_value, '{"code": 404, "message": "Package not found"}') + else + call json_loads(j_value, '{"code": 200, "data": {"version_data": {"version": "0.1.0", "download_url": "abc"}}}') + end if else call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {"version": "0.1.0", "download_url": "abc"}}}') end if From 088ae494d972455d4e8ac0b626ef3e0a9dfbfea0 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 19:05:28 +0100 Subject: [PATCH 136/799] Add tests for checking and reading package data --- src/fpm/dependency.f90 | 14 +- test/fpm_test/test_package_dependencies.f90 | 260 +++++++++++++++++++- 2 files changed, 267 insertions(+), 7 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index baba22a6be..726460a4c7 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -73,7 +73,8 @@ module fpm_dependency implicit none private - public :: dependency_tree_t, new_dependency_tree, dependency_node_t, new_dependency_node, resize + public :: dependency_tree_t, new_dependency_tree, dependency_node_t, new_dependency_node, resize, & + & check_and_read_pkg_data !> Overloaded reallocation interface interface resize @@ -551,7 +552,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade close (unit, status='delete') if (allocated(error)) return - ! Verify package data read relevant information. + ! Verify package data and read relevant information. call check_and_read_pkg_data(json, self, target_url, version, error) if (allocated(error)) return @@ -624,7 +625,7 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) call get_value(json, 'data', p, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to retrieve package data for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to read package data for '"//join_path(node%namespace, node%name)//"'."); return end if if (allocated(node%requested_version)) then @@ -648,7 +649,7 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) call get_value(q, 'download_url', download_url, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to retrieve download url for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to read download url for '"//join_path(node%namespace, node%name)//"'."); return end if download_url = official_registry_base_url//download_url @@ -659,12 +660,13 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) call get_value(q, 'version', version_str, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to retrieve version data for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to read version data for '"//join_path(node%namespace, node%name)//"'."); return end if call new_version(version, version_str, error) if (allocated(error)) then - call fatal_error(error, "Invalid version: '"//version_str//"'."); return + call fatal_error(error, "'"//version_str//"' is not a valid version for '"// & + & join_path(node%namespace, node%name)//"'."); return end if end subroutine diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 76052162f9..6664af88e6 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -57,7 +57,22 @@ subroutine collect_package_dependencies(tests) & new_unittest("version-found-in-cache", version_found_in_cache), & & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & & new_unittest("no-version-in-cache-or-registry", no_version_in_cache_or_registry, should_fail=.true.), & - & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache) & + & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache), & + & new_unittest("pkg-data-no-code", pkg_data_no_code, should_fail=.true.), & + & new_unittest("pkg-data-corrupt-code", pkg_data_corrupt_code, should_fail=.true.), & + & new_unittest("pkg-data-missing-error-message", pkg_data_missing_error_msg, should_fail=.true.), & + & new_unittest("pkg-data-error-reading-message", pkg_data_error_reading_msg, should_fail=.true.), & + & new_unittest("pkg-data-error-has-message", pkg_data_error_has_msg, should_fail=.true.), & + & new_unittest("pkg-data-error-no-data", pkg_data_no_data, should_fail=.true.), & + & new_unittest("pkg-data-error-reading-data", pkg_data_error_reading_data, should_fail=.true.), & + & new_unittest("pkg-data-requested-version-wrong-key", pkg_data_requested_version_wrong_key, should_fail=.true.), & + & new_unittest("pkg-data-no-version-requested-wrong-key", pkg_data_no_version_requested_wrong_key, should_fail=.true.), & + & new_unittest("pkg-data-error-reading-latest-version", pkg_data_error_reading_latest_version, should_fail=.true.), & + & new_unittest("pkg-data-no-download-url", pkg_data_no_download_url, should_fail=.true.), & + & new_unittest("pkg-data-error-reading-donwload-url", pkg_data_error_reading_download_url, should_fail=.true.), & + & new_unittest("pkg-data-no-version", pkg_data_no_version, should_fail=.true.), & + & new_unittest("pkg-data-error-reading-version", pkg_data_error_reading_version, should_fail=.true.), & + & new_unittest("pkg-data-invalid-version", pkg_data_invalid_version, should_fail=.true.) & & ] end subroutine collect_package_dependencies @@ -920,6 +935,249 @@ subroutine other_versions_in_default_cache(error) end subroutine other_versions_in_default_cache + !> Package data returned from the registry does not contain a code field. + subroutine pkg_data_no_code(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_no_code + + !> Error reading status code from package data. + subroutine pkg_data_corrupt_code(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": "integer expected"}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_corrupt_code + + subroutine pkg_data_missing_error_msg(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 123}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_missing_error_msg + + subroutine pkg_data_error_reading_msg(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 123, "message": 123}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_error_reading_msg + + subroutine pkg_data_error_has_msg(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 123, "message": "Really bad error message"}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_error_has_msg + + subroutine pkg_data_no_data(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_no_data + + subroutine pkg_data_error_reading_data(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": 123}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_error_reading_data + + subroutine pkg_data_requested_version_wrong_key(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + allocate (node%requested_version) + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": 123}}') ! Expected key: "version_data" + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_requested_version_wrong_key + + subroutine pkg_data_no_version_requested_wrong_key(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"version_data": 123}}') ! Expected key: "latest_version_data" + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_no_version_requested_wrong_key + + subroutine pkg_data_error_reading_latest_version(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": 123}}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_error_reading_latest_version + + subroutine pkg_data_no_download_url(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {}}}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_no_download_url + + subroutine pkg_data_error_reading_download_url(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {"download_url": 123}}}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_error_reading_download_url + + subroutine pkg_data_no_version(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {"download_url": "abc"}}}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_no_version + + subroutine pkg_data_error_reading_version(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {"download_url": "abc", "version": 123}}}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_error_reading_version + + subroutine pkg_data_invalid_version(error) + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: node + character(:), allocatable :: url + type(version_t) :: version + type(json_object) :: json + class(json_value), allocatable :: j_value + + call json_loads(j_value, '{"code": 200, "data": {"latest_version_data": {"download_url": "abc", "version": "abc"}}}') + json = cast_to_object(j_value) + + call check_and_read_pkg_data(json, node, url, version, error) + + end subroutine pkg_data_invalid_version + !> Resolve a single dependency node subroutine resolve_dependency_once(self, dependency, root, error) !> Mock instance of the dependency tree From 3c03802aedf91be484576e9eff32e9f19fa4c77c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 21:02:46 +0100 Subject: [PATCH 137/799] Improve test names --- test/fpm_test/test_package_dependencies.f90 | 148 ++++++++++---------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 6664af88e6..6ff18cc670 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -46,18 +46,18 @@ subroutine collect_package_dependencies(tests) & new_unittest("add-dependencies", test_add_dependencies), & & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & - & new_unittest("version-not-found-in-registry", version_not_found_in_registry, should_fail=.true.), & - & new_unittest("version-found-without-manifest", version_found_without_manifest, should_fail=.true.), & - & new_unittest("version-found-with-manifest", version_found_with_manifest), & - & new_unittest("not-a-dir", not_a_dir, should_fail=.true.), & - & new_unittest("no-versions-found", no_versions_found, should_fail=.true.), & - & new_unittest("newest-version-without-manifest", newest_version_without_manifest, should_fail=.true.), & - & new_unittest("newest-version-with-manifest", newest_version_with_manifest), & - & new_unittest("get-newest-version-from-registry", get_newest_version_from_registry), & - & new_unittest("version-found-in-cache", version_found_in_cache), & - & new_unittest("no-version-in-default-cache", no_version_in_default_cache), & - & new_unittest("no-version-in-cache-or-registry", no_version_in_cache_or_registry, should_fail=.true.), & - & new_unittest("other-versions-in-default-cache", other_versions_in_default_cache), & + & new_unittest("local-registry-specified-version-not-found", local_registry_specified_version_not_found, should_fail=.true.), & + & new_unittest("local-registry-specified-no-manifest", local_registry_specified_no_manifest, should_fail=.true.), & + & new_unittest("local-registry-specified-has-manifest", local_registry_specified_has_manifest), & + & new_unittest("local-registry-specified-not-a-dir", local_registry_specified_not_a_dir, should_fail=.true.), & + & new_unittest("local-registry-unspecified-no-versions", local_registry_unspecified_no_versions, should_fail=.true.), & + & new_unittest("local-registry-unspecified-no-manifest", local_registry_unspecified_no_manifest, should_fail=.true.), & + & new_unittest("local-registry-unspecified-has-manifest", local_registry_unspecified_has_manifest), & + & new_unittest("cache-specified-version-found", cache_specified_version_found), & + & new_unittest("specified-version-not-found-in-cache", registry_specified_version_not_found_in_cache), & + & new_unittest("registry-specified-version-not-exists-anywhere", registry_specified_version_not_exists_anywhere, should_fail=.true.), & + & new_unittest("registry-specified-version-other-versions-exist", registry_specified_version_other_versions_exist), & + & new_unittest("registry-unspecified-version", registry_unspecified_version), & & new_unittest("pkg-data-no-code", pkg_data_no_code, should_fail=.true.), & & new_unittest("pkg-data-corrupt-code", pkg_data_corrupt_code, should_fail=.true.), & & new_unittest("pkg-data-missing-error-message", pkg_data_missing_error_msg, should_fail=.true.), & @@ -338,8 +338,8 @@ subroutine no_versions_in_registry(error) end subroutine no_versions_in_registry - !> Specific version not found in path registry. - subroutine version_not_found_in_registry(error) + !> Specific version not found in the local registry. + subroutine local_registry_specified_version_not_found(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -381,10 +381,10 @@ subroutine version_not_found_in_registry(error) call delete_tmp_folder - end subroutine version_not_found_in_registry + end subroutine local_registry_specified_version_not_found !> Target package in path registry does not contain manifest. - subroutine version_found_without_manifest(error) + subroutine local_registry_specified_no_manifest(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -427,10 +427,10 @@ subroutine version_found_without_manifest(error) call delete_tmp_folder - end subroutine version_found_without_manifest + end subroutine local_registry_specified_no_manifest !> Target package in path registry contains manifest. - subroutine version_found_with_manifest(error) + subroutine local_registry_specified_has_manifest(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -484,10 +484,10 @@ subroutine version_found_with_manifest(error) call delete_tmp_folder - end subroutine version_found_with_manifest + end subroutine local_registry_specified_has_manifest !> Target is a file, not a directory. - subroutine not_a_dir(error) + subroutine local_registry_specified_not_a_dir(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -528,11 +528,11 @@ subroutine not_a_dir(error) call delete_tmp_folder - end subroutine not_a_dir + end subroutine local_registry_specified_not_a_dir !> Try fetching the latest version in the local registry, but none are found. !> Compared to no-versions-in-registry, we aren't requesting a specific version here. - subroutine no_versions_found(error) + subroutine local_registry_unspecified_no_versions(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -572,10 +572,10 @@ subroutine no_versions_found(error) call delete_tmp_folder - end subroutine no_versions_found + end subroutine local_registry_unspecified_no_versions !> Latest version in the local registry does not have a manifest. - subroutine newest_version_without_manifest(error) + subroutine local_registry_unspecified_no_manifest(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -627,10 +627,10 @@ subroutine newest_version_without_manifest(error) call delete_tmp_folder - end subroutine newest_version_without_manifest + end subroutine local_registry_unspecified_no_manifest !> Latest version in the local registry has a manifest. - subroutine newest_version_with_manifest(error) + subroutine local_registry_unspecified_has_manifest(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -683,31 +683,33 @@ subroutine newest_version_with_manifest(error) call delete_tmp_folder - end subroutine newest_version_with_manifest + end subroutine local_registry_unspecified_has_manifest - !> No version specified, get the newest version from the registry. - subroutine get_newest_version_from_registry(error) + !> Version specified in manifest, version found in cache. + subroutine cache_specified_version_found(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd + character(len=:), allocatable :: target_dir, cwd, path type(toml_table), pointer :: child - type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') + call set_value(table, 'v', '2.3.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return call delete_tmp_folder - call mkdir(tmp_folder) + path = join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0') + call mkdir(path) + call filewrite(join_path(path, 'fpm.toml'), ['']) call new_table(table) - call add_table(table, 'registry', child) + call add_table(table, 'registry', child) ! No cache_path specified, use default call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -719,7 +721,7 @@ subroutine get_newest_version_from_registry(error) call delete_tmp_folder; return end if - call node%get_from_registry(target_dir, global_settings, error, mock_downloader) + call node%get_from_registry(target_dir, global_settings, error) if (allocated(error)) then call delete_tmp_folder; return end if @@ -729,40 +731,39 @@ subroutine get_newest_version_from_registry(error) call delete_tmp_folder; return end if - if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0'))) then call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") call delete_tmp_folder; return end if call delete_tmp_folder - end subroutine get_newest_version_from_registry + end subroutine cache_specified_version_found - !> Version specified in manifest, version found in cache. - subroutine version_found_in_cache(error) + !> Version specified in manifest, but not found in cache. Therefore download dependency. + subroutine registry_specified_version_not_found_in_cache(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd, path + character(len=:), allocatable :: target_dir, cwd type(toml_table), pointer :: child + type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '2.3.0') + call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return call delete_tmp_folder - path = join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0') - call mkdir(path) - call filewrite(join_path(path, 'fpm.toml'), ['']) + call mkdir(tmp_folder) ! Dependencies folder doesn't exist call new_table(table) - call add_table(table, 'registry', child) ! No cache_path specified, use default + call add_table(table, 'registry', child) call setup_global_settings(global_settings, error) if (allocated(error)) then @@ -774,7 +775,7 @@ subroutine version_found_in_cache(error) call delete_tmp_folder; return end if - call node%get_from_registry(target_dir, global_settings, error) + call node%get_from_registry(target_dir, global_settings, error, mock_downloader) if (allocated(error)) then call delete_tmp_folder; return end if @@ -784,36 +785,36 @@ subroutine version_found_in_cache(error) call delete_tmp_folder; return end if - if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.3.0'))) then + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") call delete_tmp_folder; return end if call delete_tmp_folder - end subroutine version_found_in_cache + end subroutine registry_specified_version_not_found_in_cache - !> Version specified in manifest, but not found in cache. Therefore download dependency. - subroutine no_version_in_default_cache(error) + !> Version specified in manifest, but not found in cache or registry. + subroutine registry_specified_version_not_exists_anywhere(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir, cwd + character(len=:), allocatable :: target_dir type(toml_table), pointer :: child type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '0.1.0') + call set_value(table, 'v', '9.9.9') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return call delete_tmp_folder - call mkdir(tmp_folder) ! Dependencies folder doesn't exist + call mkdir(tmp_folder) call new_table(table) call add_table(table, 'registry', child) @@ -833,22 +834,11 @@ subroutine no_version_in_default_cache(error) call delete_tmp_folder; return end if - call get_current_directory(cwd, error) - if (allocated(error)) then - call delete_tmp_folder; return - end if - - if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then - call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") - call delete_tmp_folder; return - end if - call delete_tmp_folder - end subroutine no_version_in_default_cache + end subroutine registry_specified_version_not_exists_anywhere - !> Version specified in manifest, but not found in cache or registry. - subroutine no_version_in_cache_or_registry(error) + subroutine registry_specified_version_other_versions_exist(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -861,13 +851,14 @@ subroutine no_version_in_cache_or_registry(error) call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '9.9.9') + call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return call delete_tmp_folder - call mkdir(tmp_folder) + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.1.0')) + call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '9.1.0')) call new_table(table) call add_table(table, 'registry', child) @@ -889,29 +880,28 @@ subroutine no_version_in_cache_or_registry(error) call delete_tmp_folder - end subroutine no_version_in_cache_or_registry + end subroutine registry_specified_version_other_versions_exist - subroutine other_versions_in_default_cache(error) + !> No version specified, get the newest version from the registry. + subroutine registry_unspecified_version(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table type(dependency_node_t) :: node type(fpm_global_settings) :: global_settings - character(len=:), allocatable :: target_dir + character(len=:), allocatable :: target_dir, cwd type(toml_table), pointer :: child type(mock_downloader_t) :: mock_downloader call new_table(table) table%key = 'test-dep' call set_value(table, 'namespace', 'test-org') - call set_value(table, 'v', '0.1.0') call new_dependency(node%dependency_config_t, table, error=error) if (allocated(error)) return call delete_tmp_folder - call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '2.1.0')) - call mkdir(join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '9.1.0')) + call mkdir(tmp_folder) call new_table(table) call add_table(table, 'registry', child) @@ -931,9 +921,19 @@ subroutine other_versions_in_default_cache(error) call delete_tmp_folder; return end if + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then + call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") + call delete_tmp_folder; return + end if + call delete_tmp_folder - end subroutine other_versions_in_default_cache + end subroutine registry_unspecified_version !> Package data returned from the registry does not contain a code field. subroutine pkg_data_no_code(error) From 6f11ee992d5a14cfc9d341f109a41894d8bab2f0 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 21:38:26 +0100 Subject: [PATCH 138/799] Always replace when download --- src/fpm/dependency.f90 | 9 +-- test/fpm_test/test_package_dependencies.f90 | 64 ++++++++++++++++++++- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 726460a4c7..6b4d914f59 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -56,9 +56,9 @@ !> Currenly ignored. First come, first serve. module fpm_dependency use, intrinsic :: iso_fortran_env, only: output_unit - use fpm_environment, only: get_os_type, OS_WINDOWS + use fpm_environment, only: get_os_type, OS_WINDOWS, os_is_unix use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename + use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, os_delete_dir use fpm_git, only: git_target_revision, git_target_default, git_revision use fpm_manifest, only: package_config_t, dependency_config_t, & get_package_data @@ -562,10 +562,11 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if - ! Include version number in the cache path. In no cached version exists, download it. + ! Include version number in the cache path. If no cached version exists, download it. cache_path = join_path(cache_path, version%s()) if (.not. exists(join_path(cache_path, 'fpm.toml'))) then - if (.not. exists(cache_path)) call mkdir(cache_path) + if (is_dir(cache_path)) call os_delete_dir(os_is_unix(), cache_path) + call mkdir(cache_path) print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." call downloader%get_file(target_url, tmp_file, error) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 6ff18cc670..9d4ff67bfd 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -55,9 +55,10 @@ subroutine collect_package_dependencies(tests) & new_unittest("local-registry-unspecified-has-manifest", local_registry_unspecified_has_manifest), & & new_unittest("cache-specified-version-found", cache_specified_version_found), & & new_unittest("specified-version-not-found-in-cache", registry_specified_version_not_found_in_cache), & - & new_unittest("registry-specified-version-not-exists-anywhere", registry_specified_version_not_exists_anywhere, should_fail=.true.), & + & new_unittest("registry-specified-version-not-exists", registry_specified_version_not_exists, should_fail=.true.), & & new_unittest("registry-specified-version-other-versions-exist", registry_specified_version_other_versions_exist), & & new_unittest("registry-unspecified-version", registry_unspecified_version), & + & new_unittest("registry-unspecified-version_exists_in_cache", registry_unspecified_version_exists_in_cache), & & new_unittest("pkg-data-no-code", pkg_data_no_code, should_fail=.true.), & & new_unittest("pkg-data-corrupt-code", pkg_data_corrupt_code, should_fail=.true.), & & new_unittest("pkg-data-missing-error-message", pkg_data_missing_error_msg, should_fail=.true.), & @@ -795,7 +796,7 @@ subroutine registry_specified_version_not_found_in_cache(error) end subroutine registry_specified_version_not_found_in_cache !> Version specified in manifest, but not found in cache or registry. - subroutine registry_specified_version_not_exists_anywhere(error) + subroutine registry_specified_version_not_exists(error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table @@ -836,7 +837,7 @@ subroutine registry_specified_version_not_exists_anywhere(error) call delete_tmp_folder - end subroutine registry_specified_version_not_exists_anywhere + end subroutine registry_specified_version_not_exists subroutine registry_specified_version_other_versions_exist(error) type(error_t), allocatable, intent(out) :: error @@ -935,6 +936,63 @@ subroutine registry_unspecified_version(error) end subroutine registry_unspecified_version + !> No version specified, therefore load package data from the registry. Find out that there is a cached version of + !> the latest package. + subroutine registry_unspecified_version_exists_in_cache(error) + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(dependency_node_t) :: node + type(fpm_global_settings) :: global_settings + character(len=:), allocatable :: target_dir, cwd + type(toml_table), pointer :: child + type(mock_downloader_t) :: mock_downloader + + call new_table(table) + table%key = 'test-dep' + call set_value(table, 'namespace', 'test-org') + + call new_dependency(node%dependency_config_t, table, error=error) + if (allocated(error)) return + + call delete_tmp_folder + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.0.0')) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0')) + call filewrite(join_path(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '0.1.0'), 'fpm.toml'), ['']) + call mkdir(join_path(tmp_folder, 'cache', 'test-org', 'test-dep', '1.2.1')) + + call new_table(table) + call add_table(table, 'registry', child) + + call setup_global_settings(global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_registry_settings(child, global_settings, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call node%get_from_registry(target_dir, global_settings, error, mock_downloader) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + call get_current_directory(cwd, error) + if (allocated(error)) then + call delete_tmp_folder; return + end if + + if (target_dir /= join_path(cwd, join_path(tmp_folder, 'dependencies', 'test-org', 'test-dep', '0.1.0'))) then + call test_failed(error, "Target directory not set correctly: '"//target_dir//"'") + call delete_tmp_folder; return + end if + + call delete_tmp_folder + + end subroutine registry_unspecified_version_exists_in_cache + !> Package data returned from the registry does not contain a code field. subroutine pkg_data_no_code(error) type(error_t), allocatable, intent(out) :: error From 352c931f91414532458f324b3a6dc43e25b46e22 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 9 Mar 2023 01:53:00 +0100 Subject: [PATCH 139/799] Fix url separator and properly inject downloader --- src/fpm/dependency.f90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 6b4d914f59..8fdbc93687 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -451,6 +451,7 @@ subroutine resolve_dependency(self, dependency, root, error) character(len=:), allocatable :: manifest, proj_dir, revision type(fpm_global_settings) :: global_settings logical :: fetch + type(downloader_t) :: downloader if (dependency%done) return @@ -468,7 +469,7 @@ subroutine resolve_dependency(self, dependency, root, error) else call get_global_settings(global_settings, error) if (allocated(error)) return - call dependency%get_from_registry(proj_dir, global_settings, error) + call dependency%get_from_registry(proj_dir, global_settings, error, downloader) if (allocated(error)) return end if @@ -513,7 +514,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade type(error_t), allocatable, intent(out) :: error !> Downloader instance. - class(downloader_t), optional, intent(in) :: downloader + class(downloader_t), intent(in) :: downloader character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path type(version_t) :: version @@ -653,7 +654,7 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) call fatal_error(error, "Failed to read download url for '"//join_path(node%namespace, node%name)//"'."); return end if - download_url = official_registry_base_url//download_url + download_url = official_registry_base_url//'/'//download_url if (.not. q%has_key('version')) then call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version found."); return From 38ab28c427dea7a8fb16ccda80798f6e8d7f341f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Mar 2023 01:22:23 +0100 Subject: [PATCH 140/799] Reintrocude optional and print more info --- src/fpm/dependency.f90 | 2 +- src/fpm/downloader.f90 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 8fdbc93687..c81c7d2f59 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -514,7 +514,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade type(error_t), allocatable, intent(out) :: error !> Downloader instance. - class(downloader_t), intent(in) :: downloader + class(downloader_t), optional, intent(in) :: downloader character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path type(version_t) :: version diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index da64dda985..34eb58b70d 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -84,6 +84,7 @@ subroutine unpack(tmp_file, destination, error) call fatal_error(error, "'tar' not installed."); return end if + print *, "Unpacking '"//tmp_file//"' to '"//destination//"' ..." call execute_command_line('tar -zxf '//tmp_file//' -C '//destination, exitstat=stat) if (stat /= 0) then From 13db2fedcd6e77849383bbee51809839e6010d74 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 10 Mar 2023 01:49:51 +0100 Subject: [PATCH 141/799] Improve safety --- src/fpm/dependency.f90 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index c81c7d2f59..a6cc14b540 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -451,7 +451,6 @@ subroutine resolve_dependency(self, dependency, root, error) character(len=:), allocatable :: manifest, proj_dir, revision type(fpm_global_settings) :: global_settings logical :: fetch - type(downloader_t) :: downloader if (dependency%done) return @@ -469,7 +468,7 @@ subroutine resolve_dependency(self, dependency, root, error) else call get_global_settings(global_settings, error) if (allocated(error)) return - call dependency%get_from_registry(proj_dir, global_settings, error, downloader) + call dependency%get_from_registry(proj_dir, global_settings, error) if (allocated(error)) return end if @@ -499,7 +498,7 @@ end subroutine resolve_dependency !> Get a dependency from the registry. Whether the dependency is fetched !> from a local, a custom remote or the official registry is determined !> by the global configuration settings. - subroutine get_from_registry(self, target_dir, global_settings, error, downloader) + subroutine get_from_registry(self, target_dir, global_settings, error, downloader_) !> Instance of the dependency configuration. class(dependency_node_t), intent(in) :: self @@ -514,12 +513,19 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade type(error_t), allocatable, intent(out) :: error !> Downloader instance. - class(downloader_t), optional, intent(in) :: downloader + class(downloader_t), optional, intent(in) :: downloader_ character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path type(version_t) :: version integer :: stat, unit type(json_object) :: json + class(downloader_t), allocatable :: downloader + + if (present(downloader_)) then + downloader = downloader_ + else + allocate(downloader) + end if ! Use local registry if it was specified in the global config file. if (allocated(global_settings%registry_settings%path)) then From fca9aba9e8df6c540a187c5ed4b2608fcae261a0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 15 Mar 2023 09:22:10 +0100 Subject: [PATCH 142/799] Add exit code example package --- example_packages/fpm_test_exit_code/README.md | 6 +++ .../fpm_test_exit_code/app/main.f90 | 42 +++++++++++++++++++ example_packages/fpm_test_exit_code/fpm.toml | 10 +++++ 3 files changed, 58 insertions(+) create mode 100644 example_packages/fpm_test_exit_code/README.md create mode 100644 example_packages/fpm_test_exit_code/app/main.f90 create mode 100644 example_packages/fpm_test_exit_code/fpm.toml diff --git a/example_packages/fpm_test_exit_code/README.md b/example_packages/fpm_test_exit_code/README.md new file mode 100644 index 0000000000..1f6757a092 --- /dev/null +++ b/example_packages/fpm_test_exit_code/README.md @@ -0,0 +1,6 @@ +# fpm_test_exit_code +Test program for application exit codes +see https://github.com/fortran-lang/fpm/issues/848 + +This app expects to receive an integer command line argument, to check whether it is odd or even. +It returns 0 on success (odd input), or among a few error codes otherwise. diff --git a/example_packages/fpm_test_exit_code/app/main.f90 b/example_packages/fpm_test_exit_code/app/main.f90 new file mode 100644 index 0000000000..af877c37af --- /dev/null +++ b/example_packages/fpm_test_exit_code/app/main.f90 @@ -0,0 +1,42 @@ +! Test program for application exit codes +! see https://github.com/fortran-lang/fpm/issues/848 + +! This app expects to receive an integer command line argument, to check whether it is odd or even. +! It returns 0 on success (odd input), or among a few error codes otherwise. + +program check_odd_number + implicit none + + integer, parameter :: SUCCESS = 0 + integer, parameter :: INVALID_ARGUMENT = 1 + integer, parameter :: NOT_AN_INTEGER = 2 + integer, parameter :: NOT_ODD = 3 + + character(len=1024) :: buffer + integer :: ierr,ln,the_number + + ! If the argument is missing or not an integer, return an error flag + if (command_argument_count()/=1) stop INVALID_ARGUMENT + + ! Get command argument + call get_command_argument(1,value=buffer,length=ln,status=ierr) + + ! On invalid string + if (ln<1 .or. ierr/=0) stop INVALID_ARGUMENT + + ! Read to int + read(buffer(:ln),*,iostat=ierr) the_number + + ! On invalid integer + if (ierr/=0) stop NOT_AN_INTEGER + + ! Check if it is odd or even + if (mod(the_number,2)==0) then + ! Is even + stop NOT_ODD + else + ! Is odd + stop SUCCESS + end if + +end program check_odd_number diff --git a/example_packages/fpm_test_exit_code/fpm.toml b/example_packages/fpm_test_exit_code/fpm.toml new file mode 100644 index 0000000000..0c2989218f --- /dev/null +++ b/example_packages/fpm_test_exit_code/fpm.toml @@ -0,0 +1,10 @@ +name = "fpm_test_exit_code" +version = "0.1.0" +license = "license" +author = "Jane Doe" +maintainer = "jane.doe@example.com" +copyright = "Copyright 2023, Jane Doe" +[build] +auto-executables = true +[install] +library = false From 1be022f5250129f3c6bc5797619df9488306d7c9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 15 Mar 2023 09:24:30 +0100 Subject: [PATCH 143/799] Update fpm.toml --- example_packages/fpm_test_exit_code/fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_packages/fpm_test_exit_code/fpm.toml b/example_packages/fpm_test_exit_code/fpm.toml index 0c2989218f..c8fc1977d0 100644 --- a/example_packages/fpm_test_exit_code/fpm.toml +++ b/example_packages/fpm_test_exit_code/fpm.toml @@ -1,7 +1,7 @@ name = "fpm_test_exit_code" version = "0.1.0" license = "license" -author = "Jane Doe" +author = "Federico Perini" maintainer = "jane.doe@example.com" copyright = "Copyright 2023, Jane Doe" [build] From 1133a03f8967caf77e2b6804154ad61c126325fe Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 15 Mar 2023 09:45:38 +0100 Subject: [PATCH 144/799] CI: add several tests for exit code checking --- ci/run_tests.sh | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index efd7370b0f..aadc984c6e 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -158,5 +158,42 @@ pushd cpp_files "$fpm" test popd +# Test app exit codes +pushd fpm_test_exit_code +"$fpm" build + +# odd number -> return 0 +EXIT_CODE=0 +"$fpm" run -- 1 || EXIT_CODE=$? +test $EXIT_CODE -eq 0 + +# even number -> return 3 +EXIT_CODE=0 +"$fpm" run -- 512 || EXIT_CODE=$? +test $EXIT_CODE -eq 3 + +# even number -> return 3 +EXIT_CODE=0 +"$fpm" run -- 0 || EXIT_CODE=$? +test $EXIT_CODE -eq 3 + +# not an integer -> return 3 +EXIT_CODE=0 +"$fpm" run -- 3.1415 || EXIT_CODE=$? +test $EXIT_CODE -eq 2 + +# not a number +EXIT_CODE=0 +"$fpm" run -- notanumber || EXIT_CODE=$? +test $EXIT_CODE -eq 2 + +# no arguments +EXIT_CODE=0 +"$fpm" run || EXIT_CODE=$? +test $EXIT_CODE -eq 1 + +popd + + # Cleanup rm -rf ./*/build From bb140c5507a90aed564c08db617656de4825586e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 15 Mar 2023 09:49:04 +0100 Subject: [PATCH 145/799] cleanup --- ci/run_tests.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index aadc984c6e..1c90b1ff39 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -162,32 +162,32 @@ popd pushd fpm_test_exit_code "$fpm" build -# odd number -> return 0 +# odd number -> success! EXIT_CODE=0 "$fpm" run -- 1 || EXIT_CODE=$? test $EXIT_CODE -eq 0 -# even number -> return 3 +# even number -> error 3 EXIT_CODE=0 "$fpm" run -- 512 || EXIT_CODE=$? test $EXIT_CODE -eq 3 -# even number -> return 3 +# even number -> error 3 EXIT_CODE=0 "$fpm" run -- 0 || EXIT_CODE=$? test $EXIT_CODE -eq 3 -# not an integer -> return 3 +# not an integer -> error 2 EXIT_CODE=0 "$fpm" run -- 3.1415 || EXIT_CODE=$? test $EXIT_CODE -eq 2 -# not a number +# not a number -> error 2 EXIT_CODE=0 "$fpm" run -- notanumber || EXIT_CODE=$? test $EXIT_CODE -eq 2 -# no arguments +# no arguments -> error 1 EXIT_CODE=0 "$fpm" run || EXIT_CODE=$? test $EXIT_CODE -eq 1 From a4813e61599887c0c6bedd5552eff70b838a6669 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 15 Mar 2023 09:56:07 +0100 Subject: [PATCH 146/799] indent, cleanup --- ci/run_tests.sh | 1 - .../fpm_test_exit_code/app/main.f90 | 50 +++++++++---------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 1c90b1ff39..ddbd3af9b2 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -191,7 +191,6 @@ test $EXIT_CODE -eq 2 EXIT_CODE=0 "$fpm" run || EXIT_CODE=$? test $EXIT_CODE -eq 1 - popd diff --git a/example_packages/fpm_test_exit_code/app/main.f90 b/example_packages/fpm_test_exit_code/app/main.f90 index af877c37af..1e9d1aaaed 100644 --- a/example_packages/fpm_test_exit_code/app/main.f90 +++ b/example_packages/fpm_test_exit_code/app/main.f90 @@ -5,38 +5,38 @@ ! It returns 0 on success (odd input), or among a few error codes otherwise. program check_odd_number - implicit none + implicit none - integer, parameter :: SUCCESS = 0 - integer, parameter :: INVALID_ARGUMENT = 1 - integer, parameter :: NOT_AN_INTEGER = 2 - integer, parameter :: NOT_ODD = 3 + integer, parameter :: SUCCESS = 0 + integer, parameter :: INVALID_ARGUMENT = 1 + integer, parameter :: NOT_AN_INTEGER = 2 + integer, parameter :: NOT_ODD = 3 - character(len=1024) :: buffer - integer :: ierr,ln,the_number + character(len=1024) :: buffer + integer :: ierr,ln,the_number - ! If the argument is missing or not an integer, return an error flag - if (command_argument_count()/=1) stop INVALID_ARGUMENT + ! If the argument is missing or not an integer, return an error flag + if (command_argument_count()/=1) stop INVALID_ARGUMENT - ! Get command argument - call get_command_argument(1,value=buffer,length=ln,status=ierr) + ! Get command argument + call get_command_argument(1,value=buffer,length=ln,status=ierr) - ! On invalid string - if (ln<1 .or. ierr/=0) stop INVALID_ARGUMENT + ! On invalid string + if (ln<1 .or. ierr/=0) stop INVALID_ARGUMENT - ! Read to int - read(buffer(:ln),*,iostat=ierr) the_number + ! Read to int + read(buffer(:ln),*,iostat=ierr) the_number - ! On invalid integer - if (ierr/=0) stop NOT_AN_INTEGER + ! On invalid integer + if (ierr/=0) stop NOT_AN_INTEGER - ! Check if it is odd or even - if (mod(the_number,2)==0) then - ! Is even - stop NOT_ODD - else - ! Is odd - stop SUCCESS - end if + ! Check if it is odd or even + if (mod(the_number,2)==0) then + ! Is even + stop NOT_ODD + else + ! Is odd + stop SUCCESS + end if end program check_odd_number From 8fb05cdee17c9810c6d028f40c86817876b3c936 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 22 Mar 2023 09:50:49 +0100 Subject: [PATCH 147/799] update gcc version to 10 --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 853d91f76b..182891dd16 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-11, windows-latest] - gcc_v: [9] # Version of GFortran we want to use. + gcc_v: [10] # Version of GFortran we want to use. include: - os: ubuntu-latest os-arch: linux-x86_64 From a5785048f3029dd861302754530eab5cb815e350 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 09:30:20 +0100 Subject: [PATCH 148/799] Create `metapackage_t` base structure --- src/fpm_meta.f90 | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/fpm_meta.f90 diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 new file mode 100644 index 0000000000..82959564e4 --- /dev/null +++ b/src/fpm_meta.f90 @@ -0,0 +1,85 @@ +!># The fpm meta-package model +!> +!> This is a wrapper data type that encapsulate all pre-processing information +!> (compiler flags, linker libraries, etc.) required to correctly enable a package +!> to use a core library. +!> +!> +!>### Available core libraries +!> +!> - OpenMP +!> +!> @note Core libraries are enabled in the [build] section of the fpm.toml manifest +!> +!> +module fpm_meta +use fpm_strings, only: string_t +use fpm_error, only: error_t, fatal_error, syntax_error +implicit none + +private + +!> Type for describing a source file +type, public :: metapackage_t + + logical :: has_link_libraries = .false. + logical :: has_compiler_flags = .false. + + !> List of compiler flags and options to be added + type(string_t), allocatable :: fflags(:) + type(string_t), allocatable :: link_flags(:) + type(string_t), allocatable :: link_dirs(:) + + contains + + !> Clean metapackage structure + procedure :: destroy + + !> Initialize the metapackage structure from its given name + procedure :: new => init_from_name + + + +end type metapackage_t + +contains + +!> Clean the metapackage structure +elemental subroutine destroy(this) + class(metapackage_t), intent(inout) :: this + + this%has_link_libraries = .false. + this%has_compiler_flags = .false. + + if (allocated(this%fflags)) deallocate(this%fflags) + if (allocated(this%link_flags)) deallocate(this%link_flags) + if (allocated(this%link_dirs)) deallocate(this%link_dirs) + +end subroutine destroy + +!> Initialize a metapackage from the given name +subroutine init_from_name(this,name,error) + class(metapackage_t), intent(inout) :: this + character(*), intent(in) :: name + type(error_t), allocatable, intent(out) :: error + + !> Initialize metapackage by name + select case(name) + case("openmp"); call init_openmp(this,error) + case default + call syntax_error(error, "Metapackage "//name//" is not supported in [build]") + return + end select + +end subroutine init_from_name + +!> Initialize OpenMP +subroutine init_openmp(this,error) + class(metapackage_t), intent(inout) :: this + type(error_t), allocatable, intent(out) :: error + + call fatal_error(error,"OpenMP metapackage is not yet supported") + +end subroutine init_openmp + +end module fpm_meta From ba28423374c1935614d49c52011e4c8ab8cd361b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 09:44:38 +0100 Subject: [PATCH 149/799] build config: introduce "openmp" flag and metapackages variable --- src/fpm/manifest/build.f90 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 8047dd045d..d4382bc709 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -13,6 +13,7 @@ module fpm_manifest_build use fpm_error, only : error_t, syntax_error, fatal_error use fpm_strings, only : string_t, len_trim, is_valid_module_prefix use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list + use fpm_meta, only : metapackage_t implicit none private @@ -34,6 +35,11 @@ module fpm_manifest_build logical :: module_naming = .false. type(string_t) :: module_prefix + !> Metapackages + !> @note when several metapackages are supported, this will need be generalized + logical :: openmp + type(metapackage_t), allocatable :: metapackages(:) + !> Libraries to link against type(string_t), allocatable :: link(:) @@ -119,6 +125,9 @@ subroutine new_build_config(self, table, error) end if + !> Metapackages: read all flags + call get_value(table, "openmp", self%openmp, .false., stat=stat) + call get_list(table, "link", self%link, error) if (allocated(error)) return @@ -153,6 +162,10 @@ subroutine check(table, error) case ("module-naming") continue + !> Supported metapackages + case ("openmp") + continue + case default call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [build]") exit From 0c4cf387012fdb4748f56ae6327bc7229f162a0b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 09:50:13 +0100 Subject: [PATCH 150/799] generate metapackages on new_build_config --- src/fpm/manifest/build.f90 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index d4382bc709..aa34c9bf9a 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -128,6 +128,9 @@ subroutine new_build_config(self, table, error) !> Metapackages: read all flags call get_value(table, "openmp", self%openmp, .false., stat=stat) + !> Generate metapackages + if (self%openmp) call add_metapackage(self,"openmp",error); if (allocated(error)) return + call get_list(table, "link", self%link, error) if (allocated(error)) return @@ -136,6 +139,28 @@ subroutine new_build_config(self, table, error) end subroutine new_build_config + !> Initialize a metapackage configuration + subroutine add_metapackage(build,name,error) + type(build_config_t), intent(inout) :: build + character(*), intent(in) :: name + type(error_t), allocatable, intent(out) :: error + + type(metapackage_t) :: add_meta + + !> Create new metapackage + call add_meta%new(name,error); if (allocated(error)) return + + !> Add it to the list of metapackages + if (allocated(build%metapackages)) then + build%metapackages = [build%metapackages, add_meta] + else + build%metapackages = [add_meta] + end if + + return + + end subroutine add_metapackage + !> Check local schema for allowed entries subroutine check(table, error) From 008244651f0ae69d82fe2e162196f8e74011d75b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 09:56:06 +0100 Subject: [PATCH 151/799] remove unused circular dependency from fpm_compiler.f90 --- src/fpm_compiler.F90 | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index dee49f9f90..2e82d902ee 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -41,7 +41,6 @@ module fpm_compiler use fpm_filesystem, only: join_path, basename, get_temp_filename, delete_file, unix_path, & & getline, run use fpm_strings, only: split, string_cat, string_t, str_ends_with, str_begins_with_str -use fpm_manifest, only : package_config_t use fpm_error, only: error_t implicit none public :: compiler_t, new_compiler, archiver_t, new_archiver, get_macros @@ -187,7 +186,7 @@ module fpm_compiler character(*), parameter :: & flag_lfortran_opt = " --fast" - + contains @@ -417,7 +416,7 @@ pure subroutine set_cpp_preprocessor_flags(id, flags) end subroutine set_cpp_preprocessor_flags -!> This function will parse and read the macros list and +!> This function will parse and read the macros list and !> return them as defined flags. function get_macros(id, macros_list, version) result(macros) integer(compiler_enum), intent(in) :: id @@ -427,7 +426,7 @@ function get_macros(id, macros_list, version) result(macros) character(len=:), allocatable :: macros character(len=:), allocatable :: macro_definition_symbol character(:), allocatable :: valued_macros(:) - + integer :: i @@ -450,10 +449,10 @@ function get_macros(id, macros_list, version) result(macros) end if do i = 1, size(macros_list) - + !> Split the macro name and value. call split(macros_list(i)%s, valued_macros, delimiters="=") - + if (size(valued_macros) > 1) then !> Check if the value of macro starts with '{' character. if (str_begins_with_str(trim(valued_macros(size(valued_macros))), "{")) then @@ -463,15 +462,15 @@ function get_macros(id, macros_list, version) result(macros) !> Check if the string contains "version" as substring. if (index(valued_macros(size(valued_macros)), "version") /= 0) then - + !> These conditions are placed in order to ensure proper spacing between the macros. macros = macros//macro_definition_symbol//trim(valued_macros(1))//'='//version cycle end if end if - end if + end if end if - + macros = macros//macro_definition_symbol//macros_list(i)%s end do @@ -794,7 +793,7 @@ subroutine new_compiler(self, fc, cc, cxx, echo, verbose) logical, intent(in) :: verbose self%id = get_compiler_id(fc) - + self%echo = echo self%verbose = verbose self%fc = fc From 24ced5a55ac94cdcffe8522c9f399f7b79fa8286 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 10:27:46 +0100 Subject: [PATCH 152/799] add default openmp flags; return human-readable compiler name --- src/fpm_compiler.F90 | 50 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 2e82d902ee..62aea27193 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -105,6 +105,8 @@ module fpm_compiler procedure :: is_unknown !> Enumerate libraries, based on compiler and platform procedure :: enumerate_libraries + !> Return compiler name + procedure :: name => compiler_name end type compiler_t @@ -139,14 +141,16 @@ module fpm_compiler flag_gnu_warn = " -Wall -Wextra -Wimplicit-interface", & flag_gnu_check = " -fcheck=bounds -fcheck=array-temps", & flag_gnu_limit = " -fmax-errors=1", & - flag_gnu_external = " -Wimplicit-interface" + flag_gnu_external = " -Wimplicit-interface", & + flag_gnu_openmp = " -fopenmp" character(*), parameter :: & flag_pgi_backslash = " -Mbackslash", & flag_pgi_traceback = " -traceback", & flag_pgi_debug = " -g", & flag_pgi_check = " -Mbounds -Mchkptr -Mchkstk", & - flag_pgi_warn = " -Minform=inform" + flag_pgi_warn = " -Minform=inform", & + flag_pgi_openmp = " -mp" character(*), parameter :: & flag_ibmxl_backslash = " -qnoescape" @@ -161,7 +165,8 @@ module fpm_compiler flag_intel_limit = " -error-limit 1", & flag_intel_pthread = " -reentrancy threaded", & flag_intel_nogen = " -nogen-interfaces", & - flag_intel_byterecl = " -assume byterecl" + flag_intel_byterecl = " -assume byterecl", & + flag_intel_openmp = " -qopenmp" character(*), parameter :: & flag_intel_backtrace_win = " /traceback", & @@ -173,7 +178,8 @@ module fpm_compiler flag_intel_limit_win = " /error-limit:1", & flag_intel_pthread_win = " /reentrancy:threaded", & flag_intel_nogen_win = " /nogen-interfaces", & - flag_intel_byterecl_win = " /assume:byterecl" + flag_intel_byterecl_win = " /assume:byterecl", & + flag_intel_openmp_win = " /Qopenmp" character(*), parameter :: & flag_nag_coarray = " -coarray=single", & @@ -181,10 +187,12 @@ module fpm_compiler flag_nag_check = " -C", & flag_nag_debug = " -g -O0", & flag_nag_opt = " -O4", & - flag_nag_backtrace = " -gline" + flag_nag_backtrace = " -gline", & + flag_nag_openmp = " -openmp" character(*), parameter :: & - flag_lfortran_opt = " --fast" + flag_lfortran_opt = " --fast", & + flag_lfortran_openmp = " --openmp" contains @@ -1015,5 +1023,35 @@ pure function debug_archiver(self) result(repr) repr = 'ar="'//self%ar//'"' end function debug_archiver +!> Return a compiler name string +type(string_t) function compiler_name(self) result(name) + !> Instance of the compiler object + class(compiler_t), intent(in) :: self + + select case (self%id) + case(id_gcc); name = string_t("gfortran") + case(id_f95); name = string_t("f95") + case(id_caf); name = string_t("caf") + case(id_intel_classic_nix); name = string_t("ifort") + case(id_intel_classic_mac); name = string_t("ifort") + case(id_intel_classic_windows); name = string_t("ifort") + case(id_intel_llvm_nix); name = string_t("ifx") + case(id_intel_llvm_windows); name = string_t("ifx") + case(id_intel_llvm_unknown); name = string_t("ifx") + case(id_pgi); name = string_t("pgfortran") + case(id_nvhpc); name = string_t("nvfortran") + case(id_nag); name = string_t("nagfor") + case(id_flang); name = string_t("flang") + case(id_flang_new); name = string_t("flang-new") + case(id_f18); name = string_t("f18") + case(id_ibmxl); name = string_t("xlf90") + case(id_cray); name = string_t("crayftn") + case(id_lahey); name = string_t("lfc") + case(id_lfortran); name = string_t("lFortran") + case default; name = string_t("invalid/unknown") + end select +end function compiler_name + + end module fpm_compiler From 222aa2a0a20d28b4c5eb4f1aa5e58973a762fa90 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 10:30:20 +0100 Subject: [PATCH 153/799] remove built metapackages from the build config --- src/fpm/manifest/build.f90 | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index aa34c9bf9a..660cfa49a4 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -37,8 +37,7 @@ module fpm_manifest_build !> Metapackages !> @note when several metapackages are supported, this will need be generalized - logical :: openmp - type(metapackage_t), allocatable :: metapackages(:) + logical :: openmp = .false. !> Libraries to link against type(string_t), allocatable :: link(:) @@ -128,9 +127,6 @@ subroutine new_build_config(self, table, error) !> Metapackages: read all flags call get_value(table, "openmp", self%openmp, .false., stat=stat) - !> Generate metapackages - if (self%openmp) call add_metapackage(self,"openmp",error); if (allocated(error)) return - call get_list(table, "link", self%link, error) if (allocated(error)) return @@ -139,28 +135,6 @@ subroutine new_build_config(self, table, error) end subroutine new_build_config - !> Initialize a metapackage configuration - subroutine add_metapackage(build,name,error) - type(build_config_t), intent(inout) :: build - character(*), intent(in) :: name - type(error_t), allocatable, intent(out) :: error - - type(metapackage_t) :: add_meta - - !> Create new metapackage - call add_meta%new(name,error); if (allocated(error)) return - - !> Add it to the list of metapackages - if (allocated(build%metapackages)) then - build%metapackages = [build%metapackages, add_meta] - else - build%metapackages = [add_meta] - end if - - return - - end subroutine add_metapackage - !> Check local schema for allowed entries subroutine check(table, error) From 9854e2d3c5b6cd6ec46793a4daccaa9e1a4d7c16 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 10:34:38 +0100 Subject: [PATCH 154/799] compiler name: make it `character(:), allocatable` --- src/fpm_compiler.F90 | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 62aea27193..fe7e2bfa7a 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -1024,31 +1024,33 @@ pure function debug_archiver(self) result(repr) end function debug_archiver !> Return a compiler name string -type(string_t) function compiler_name(self) result(name) +pure function compiler_name(self) result(name) !> Instance of the compiler object class(compiler_t), intent(in) :: self + !> Representation as string + character(len=:), allocatable :: name select case (self%id) - case(id_gcc); name = string_t("gfortran") - case(id_f95); name = string_t("f95") - case(id_caf); name = string_t("caf") - case(id_intel_classic_nix); name = string_t("ifort") - case(id_intel_classic_mac); name = string_t("ifort") - case(id_intel_classic_windows); name = string_t("ifort") - case(id_intel_llvm_nix); name = string_t("ifx") - case(id_intel_llvm_windows); name = string_t("ifx") - case(id_intel_llvm_unknown); name = string_t("ifx") - case(id_pgi); name = string_t("pgfortran") - case(id_nvhpc); name = string_t("nvfortran") - case(id_nag); name = string_t("nagfor") - case(id_flang); name = string_t("flang") - case(id_flang_new); name = string_t("flang-new") - case(id_f18); name = string_t("f18") - case(id_ibmxl); name = string_t("xlf90") - case(id_cray); name = string_t("crayftn") - case(id_lahey); name = string_t("lfc") - case(id_lfortran); name = string_t("lFortran") - case default; name = string_t("invalid/unknown") + case(id_gcc); name = "gfortran" + case(id_f95); name = "f95" + case(id_caf); name = "caf" + case(id_intel_classic_nix); name = "ifort" + case(id_intel_classic_mac); name = "ifort" + case(id_intel_classic_windows); name = "ifort" + case(id_intel_llvm_nix); name = "ifx" + case(id_intel_llvm_windows); name = "ifx" + case(id_intel_llvm_unknown); name = "ifx" + case(id_pgi); name = "pgfortran" + case(id_nvhpc); name = "nvfortran" + case(id_nag); name = "nagfor" + case(id_flang); name = "flang" + case(id_flang_new); name = "flang-new" + case(id_f18); name = "f18" + case(id_ibmxl); name = "xlf90" + case(id_cray); name = "crayftn" + case(id_lahey); name = "lfc" + case(id_lfortran); name = "lFortran" + case default; name = "invalid/unknown" end select end function compiler_name From 59a9d73fe1c25f0ba7404020bd16f4aa809a2eb3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 10:45:42 +0100 Subject: [PATCH 155/799] code openmp compiler flags --- src/fpm_meta.f90 | 68 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 82959564e4..eeb325d213 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -15,6 +15,7 @@ module fpm_meta use fpm_strings, only: string_t use fpm_error, only: error_t, fatal_error, syntax_error +use fpm_compiler implicit none private @@ -23,10 +24,11 @@ module fpm_meta type, public :: metapackage_t logical :: has_link_libraries = .false. - logical :: has_compiler_flags = .false. + logical :: has_link_flags = .false. + logical :: has_build_flags = .false. !> List of compiler flags and options to be added - type(string_t), allocatable :: fflags(:) + type(string_t), allocatable :: flags(:) type(string_t), allocatable :: link_flags(:) type(string_t), allocatable :: link_dirs(:) @@ -49,23 +51,25 @@ elemental subroutine destroy(this) class(metapackage_t), intent(inout) :: this this%has_link_libraries = .false. - this%has_compiler_flags = .false. + this%has_link_flags = .false. + this%has_build_flags = .false. - if (allocated(this%fflags)) deallocate(this%fflags) + if (allocated(this%flags)) deallocate(this%flags) if (allocated(this%link_flags)) deallocate(this%link_flags) if (allocated(this%link_dirs)) deallocate(this%link_dirs) end subroutine destroy !> Initialize a metapackage from the given name -subroutine init_from_name(this,name,error) +subroutine init_from_name(this,name,compiler,error) class(metapackage_t), intent(inout) :: this character(*), intent(in) :: name + type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error !> Initialize metapackage by name select case(name) - case("openmp"); call init_openmp(this,error) + case("openmp"); call init_openmp(this,compiler,error) case default call syntax_error(error, "Metapackage "//name//" is not supported in [build]") return @@ -73,12 +77,58 @@ subroutine init_from_name(this,name,error) end subroutine init_from_name -!> Initialize OpenMP -subroutine init_openmp(this,error) +!> Initialize OpenMP metapackage for the current system +subroutine init_openmp(this,compiler,error) class(metapackage_t), intent(inout) :: this + type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - call fatal_error(error,"OpenMP metapackage is not yet supported") + character(:), allocatable :: flags + + !> Cleanup + call destroy(this) + + !> OpenMP has compiler flags + this%has_build_flags = .true. + this%has_link_flags = .true. + + !> OpenMP flags should be added to + which_compiler: select case (compiler%id) + case (id_gcc,id_f95) + this%flags = [string_t(flag_gnu_openmp)] + this%link_flags = [string_t(flag_gnu_openmp)] + + case (id_intel_classic_windows,id_intel_llvm_windows) + this%flags = [string_t(flag_intel_openmp_win)] + this%link_flags = [string_t(flag_intel_openmp_win)] + + case (id_intel_classic_nix,id_intel_classic_mac,& + id_intel_llvm_nix) + this%flags = [string_t(flag_intel_openmp)] + this%link_flags = [string_t(flag_intel_openmp)] + + case (id_pgi,id_nvhpc) + this%flags = [string_t(flag_pgi_openmp)] + this%link_flags = [string_t(flag_pgi_openmp)] + + case (id_ibmxl) + this%flags = [string_t(" -qsmp=omp")] + this%link_flags = [string_t(" -qsmp=omp")] + + case (id_nag) + this%flags = [string_t(flag_nag_openmp)] + this%link_flags = [string_t(flag_nag_openmp)] + + case (id_lfortran) + this%flags = [string_t(flag_lfortran_openmp)] + this%link_flags = [string_t(flag_lfortran_openmp)] + + case default + + call fatal_error(error,'openmp not supported on compiler '//compiler%name()//' yet') + + end select which_compiler + end subroutine init_openmp From 53799ab99946fb2231ed7fc0a72df3478b65bd1b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 11:15:18 +0100 Subject: [PATCH 156/799] basic metapackage resolution --- src/fpm/manifest/build.f90 | 1 - src/fpm_meta.f90 | 93 ++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 660cfa49a4..cc25647fca 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -13,7 +13,6 @@ module fpm_manifest_build use fpm_error, only : error_t, syntax_error, fatal_error use fpm_strings, only : string_t, len_trim, is_valid_module_prefix use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list - use fpm_meta, only : metapackage_t implicit none private diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index eeb325d213..00dc745c87 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -16,21 +16,26 @@ module fpm_meta use fpm_strings, only: string_t use fpm_error, only: error_t, fatal_error, syntax_error use fpm_compiler +use fpm_model implicit none private +public :: add_metapackage + !> Type for describing a source file type, public :: metapackage_t logical :: has_link_libraries = .false. logical :: has_link_flags = .false. logical :: has_build_flags = .false. + logical :: has_include_dirs = .false. !> List of compiler flags and options to be added - type(string_t), allocatable :: flags(:) - type(string_t), allocatable :: link_flags(:) + type(string_t) :: flags + type(string_t) :: link_flags type(string_t), allocatable :: link_dirs(:) + type(string_t), allocatable :: link_libs(:) contains @@ -40,6 +45,9 @@ module fpm_meta !> Initialize the metapackage structure from its given name procedure :: new => init_from_name + !> Add metapackage dependencies to the model + procedure :: resolve + end type metapackage_t @@ -53,10 +61,12 @@ elemental subroutine destroy(this) this%has_link_libraries = .false. this%has_link_flags = .false. this%has_build_flags = .false. + this%has_include_dirs = .false. - if (allocated(this%flags)) deallocate(this%flags) - if (allocated(this%link_flags)) deallocate(this%link_flags) + if (allocated(this%flags%s)) deallocate(this%flags%s) + if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) if (allocated(this%link_dirs)) deallocate(this%link_dirs) + if (allocated(this%link_libs)) deallocate(this%link_libs) end subroutine destroy @@ -83,8 +93,6 @@ subroutine init_openmp(this,compiler,error) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - character(:), allocatable :: flags - !> Cleanup call destroy(this) @@ -95,33 +103,33 @@ subroutine init_openmp(this,compiler,error) !> OpenMP flags should be added to which_compiler: select case (compiler%id) case (id_gcc,id_f95) - this%flags = [string_t(flag_gnu_openmp)] - this%link_flags = [string_t(flag_gnu_openmp)] + this%flags = string_t(flag_gnu_openmp) + this%link_flags = string_t(flag_gnu_openmp) case (id_intel_classic_windows,id_intel_llvm_windows) - this%flags = [string_t(flag_intel_openmp_win)] - this%link_flags = [string_t(flag_intel_openmp_win)] + this%flags = string_t(flag_intel_openmp_win) + this%link_flags = string_t(flag_intel_openmp_win) case (id_intel_classic_nix,id_intel_classic_mac,& id_intel_llvm_nix) - this%flags = [string_t(flag_intel_openmp)] - this%link_flags = [string_t(flag_intel_openmp)] + this%flags = string_t(flag_intel_openmp) + this%link_flags = string_t(flag_intel_openmp) case (id_pgi,id_nvhpc) - this%flags = [string_t(flag_pgi_openmp)] - this%link_flags = [string_t(flag_pgi_openmp)] + this%flags = string_t(flag_pgi_openmp) + this%link_flags = string_t(flag_pgi_openmp) case (id_ibmxl) - this%flags = [string_t(" -qsmp=omp")] - this%link_flags = [string_t(" -qsmp=omp")] + this%flags = string_t(" -qsmp=omp") + this%link_flags = string_t(" -qsmp=omp") case (id_nag) - this%flags = [string_t(flag_nag_openmp)] - this%link_flags = [string_t(flag_nag_openmp)] + this%flags = string_t(flag_nag_openmp) + this%link_flags = string_t(flag_nag_openmp) case (id_lfortran) - this%flags = [string_t(flag_lfortran_openmp)] - this%link_flags = [string_t(flag_lfortran_openmp)] + this%flags = string_t(flag_lfortran_openmp) + this%link_flags = string_t(flag_lfortran_openmp) case default @@ -132,4 +140,49 @@ subroutine init_openmp(this,compiler,error) end subroutine init_openmp +! Resolve metapackage dependencies into the model +subroutine resolve(self,model,error) + class(metapackage_t), intent(in) :: self + type(fpm_model_t), intent(inout) :: model + type(error_t), allocatable, intent(out) :: error + + ! For now, additional flags are assumed to apply to all sources + if (self%has_build_flags) then + model%fortran_compile_flags = model%fortran_compile_flags//self%flags%s + model%c_compile_flags = model%c_compile_flags//self%flags%s + model%cxx_compile_flags = model%cxx_compile_flags//self%flags%s + endif + + if (self%has_link_flags) then + model%link_flags = model%link_flags//self%link_flags%s + end if + + if (self%has_link_libraries) then + model%link_libraries = [model%link_libraries,self%link_libs] + end if + + if (self%has_include_dirs) then + model%include_dirs = [model%include_dirs,self%link_dirs] + end if + +end subroutine resolve + +! Add named metapackage dependency to the model +subroutine add_metapackage(model,name,error) + type(fpm_model_t), intent(inout) :: model + character(*), intent(in) :: name + type(error_t), allocatable, intent(out) :: error + + type(metapackage_t) :: meta + + !> Init metapackage + call meta%new(name,model%compiler,error) + if (allocated(error)) return + + !> Add it to the model + call meta%resolve(model,error) + if (allocated(error)) return + +end subroutine add_metapackage + end module fpm_meta From 02923637d8f821e46067759ad69b9fb32c3b1942 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 11:15:33 +0100 Subject: [PATCH 157/799] openmp metapackage is now resolved --- src/fpm.f90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/fpm.f90 b/src/fpm.f90 index 51a1bb16f5..82c97758db 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -21,6 +21,7 @@ module fpm resolve_target_linking, build_target_t, build_target_ptr, & FPM_TARGET_EXECUTABLE, FPM_TARGET_ARCHIVE use fpm_manifest, only : get_package_data, package_config_t +use fpm_meta, only : add_metapackage use fpm_error, only : error_t, fatal_error, fpm_stop use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, & & stdout=>output_unit, & @@ -175,6 +176,10 @@ subroutine build_model(model, settings, package, error) model%cxx_compile_flags = cxxflags model%link_flags = ldflags + ! Build and resolve metapackage dependencies + if (package%build%openmp) call add_metapackage(model,"openmp",error) + if (allocated(error)) return + ! Add sources from executable directories if (is_dir('app') .and. package%build%auto_executables) then call add_sources_from_dir(model%packages(1)%sources,'app', FPM_SCOPE_APP, & From 7312a99ccbf2bdb18067da8e99ffc2b1a9daffcb Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 11:16:21 +0100 Subject: [PATCH 158/799] add openmp example program --- example_packages/metapackage_openmp/README.md | 4 ++++ example_packages/metapackage_openmp/app/main.f90 | 16 ++++++++++++++++ example_packages/metapackage_openmp/fpm.toml | 13 +++++++++++++ .../metapackage_openmp/src/test_openmp.f90 | 11 +++++++++++ .../metapackage_openmp/test/check.f90 | 5 +++++ 5 files changed, 49 insertions(+) create mode 100644 example_packages/metapackage_openmp/README.md create mode 100644 example_packages/metapackage_openmp/app/main.f90 create mode 100644 example_packages/metapackage_openmp/fpm.toml create mode 100644 example_packages/metapackage_openmp/src/test_openmp.f90 create mode 100644 example_packages/metapackage_openmp/test/check.f90 diff --git a/example_packages/metapackage_openmp/README.md b/example_packages/metapackage_openmp/README.md new file mode 100644 index 0000000000..e191664959 --- /dev/null +++ b/example_packages/metapackage_openmp/README.md @@ -0,0 +1,4 @@ +# test_openmp +This test program prints the running thread ID using OpenMP. +Module omp_lib is invoked, so, this code cannot build if the OpenMP library +is not properly enabled by the compiler flags. diff --git a/example_packages/metapackage_openmp/app/main.f90 b/example_packages/metapackage_openmp/app/main.f90 new file mode 100644 index 0000000000..21b0aef90f --- /dev/null +++ b/example_packages/metapackage_openmp/app/main.f90 @@ -0,0 +1,16 @@ +! OpenMP test case +! This test program will only run if openmp is properly enabled in the compiler flags. +! Otherwise, the omp_lib module won't be found and the code cannot be built. +program openmp_test + use test_openmp, only: say_hello + use omp_lib + implicit none + +!$omp parallel + call say_hello(thread_ID=OMP_GET_THREAD_NUM()) +!$omp end parallel + +! Successful return +stop 0 + +end program openmp_test diff --git a/example_packages/metapackage_openmp/fpm.toml b/example_packages/metapackage_openmp/fpm.toml new file mode 100644 index 0000000000..92105e5d8c --- /dev/null +++ b/example_packages/metapackage_openmp/fpm.toml @@ -0,0 +1,13 @@ +name = "test_openmp" +version = "0.1.0" +license = "license" +author = "Federico Perini" +maintainer = "federico.perini@hello.world" +copyright = "Copyright 2023, Federico Perini and the fpm maintainers" +[build] +auto-executables = true +auto-tests = true +auto-examples = true +openmp = true +[install] +library = false diff --git a/example_packages/metapackage_openmp/src/test_openmp.f90 b/example_packages/metapackage_openmp/src/test_openmp.f90 new file mode 100644 index 0000000000..d83b4b1605 --- /dev/null +++ b/example_packages/metapackage_openmp/src/test_openmp.f90 @@ -0,0 +1,11 @@ +module test_openmp + implicit none + private + + public :: say_hello +contains + subroutine say_hello(thread_ID) + integer, intent(in) :: thread_ID + print "(a,i0,a)", "Hello, test_openmp is called from thread ",thread_ID,"!" + end subroutine say_hello +end module test_openmp diff --git a/example_packages/metapackage_openmp/test/check.f90 b/example_packages/metapackage_openmp/test/check.f90 new file mode 100644 index 0000000000..d7e3cba687 --- /dev/null +++ b/example_packages/metapackage_openmp/test/check.f90 @@ -0,0 +1,5 @@ +program check +implicit none + +print *, "Put some tests in here!" +end program check From 62bc5734c138d0493b94e27707593312e8f854ac Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 11:21:12 +0100 Subject: [PATCH 159/799] add openmp test to CI --- ci/run_tests.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index ddbd3af9b2..2f46b0f0de 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -193,6 +193,13 @@ EXIT_CODE=0 test $EXIT_CODE -eq 1 popd +# Test metapackages +pushd metapackage_openmp +"$fpm" build +EXIT_CODE=0 +"$fpm" run || EXIT_CODE=$? +test $EXIT_CODE -eq 0 +popd # Cleanup rm -rf ./*/build From 91f8411e8813691286a8d38f8032c4bbc88d6632 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 14:16:30 +0100 Subject: [PATCH 160/799] document return of exit codes (#852) --- PACKAGING.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/PACKAGING.md b/PACKAGING.md index 663db62c52..16a3f990d0 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -128,14 +128,40 @@ $ fpm run pi = 3.14159274 ``` +Although we have named our program `hello`, which is the same name as the +package name in `fpm.toml`, you can name it anything you want as long as it’s +permitted by the language. + Notice that you can run `fpm run`, and if the package hasn’t been built yet, `fpm build` will run automatically for you. This is true if the source files have been updated since the last build. Thus, if you want to run your application, you can skip the `fpm build` step, and go straight to `fpm run`. -Although we have named our program `hello`, which is the same name as the -package name in `fpm.toml`, you can name it anything you want as long as it’s -permitted by the language. +When running your application using `fpm run`, the program's exit code is +passed by *fpm* back to the operating system. So, it is possible to use Fortran +numbered `stop` and `error stop` codes to pass termination reasons back to the terminal. + +Try running the following app with `fpm run`: + +```fortran +program main + use math_constants, only: pi + + real :: angle + + read(*,*,iostat=ierr) angle + if (ierr/=0) then + stop 2 ! Not real + elseif (angle>pi) then + stop 1 + else + stop 0 + endif +end program main +``` + +and then checking that the error code matches. Note that error codes are passed to variable `$?` +on Unix/Mac systems, and to environment variable `%errorlevel%` on Windoes. In this last example, our source file defined a `math_constants` module inside the same source file as the main program. Let’s see how we can define an *fpm* From 38d41eb447afd748a1c92534a24f98cf5fc521be Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 23 Mar 2023 14:44:30 +0100 Subject: [PATCH 161/799] typo --- PACKAGING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PACKAGING.md b/PACKAGING.md index 16a3f990d0..32bc51bfda 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -161,7 +161,7 @@ end program main ``` and then checking that the error code matches. Note that error codes are passed to variable `$?` -on Unix/Mac systems, and to environment variable `%errorlevel%` on Windoes. +on Unix/Mac systems, and to environment variable `%errorlevel%` on Windows. In this last example, our source file defined a `math_constants` module inside the same source file as the main program. Let’s see how we can define an *fpm* From 19dd707e2f7bb378ee2033f4265f2e5059799f6c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 08:42:42 +0200 Subject: [PATCH 162/799] add `stdlib` support flag --- src/fpm.f90 | 4 ++++ src/fpm/manifest/build.f90 | 17 ++++++++++++++++- src/fpm_meta.f90 | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 82c97758db..060abd5b38 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -180,6 +180,10 @@ subroutine build_model(model, settings, package, error) if (package%build%openmp) call add_metapackage(model,"openmp",error) if (allocated(error)) return + ! Stdlib is available but not implemented yet + if (package%build%stdlib) call fatal_error(error,"stdlib is not implemented yet") + if (allocated(error)) return + ! Add sources from executable directories if (is_dir('app') .and. package%build%auto_executables) then call add_sources_from_dir(model%packages(1)%sources,'app', FPM_SCOPE_APP, & diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index cc25647fca..3d505b93a2 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -36,8 +36,13 @@ module fpm_manifest_build !> Metapackages !> @note when several metapackages are supported, this will need be generalized + + !> Request OpenMP support logical :: openmp = .false. + !> Request stdlib support + logical :: stdlib = .false. + !> Libraries to link against type(string_t), allocatable :: link(:) @@ -125,6 +130,16 @@ subroutine new_build_config(self, table, error) !> Metapackages: read all flags call get_value(table, "openmp", self%openmp, .false., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'openmp' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "stdlib", self%stdlib, .false., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'stdlib' in fpm.toml, expecting logical") + return + end if call get_list(table, "link", self%link, error) if (allocated(error)) return @@ -161,7 +176,7 @@ subroutine check(table, error) continue !> Supported metapackages - case ("openmp") + case ("openmp","stdlib") continue case default diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 00dc745c87..21844a5a59 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -80,6 +80,7 @@ subroutine init_from_name(this,name,compiler,error) !> Initialize metapackage by name select case(name) case("openmp"); call init_openmp(this,compiler,error) + case("stdlib"); call init_stdlib(this,compiler,error) case default call syntax_error(error, "Metapackage "//name//" is not supported in [build]") return @@ -140,6 +141,20 @@ subroutine init_openmp(this,compiler,error) end subroutine init_openmp +!> Initialize stdlib metapackage for the current system +subroutine init_stdlib(this,compiler,error) + class(metapackage_t), intent(inout) :: this + type(compiler_t), intent(in) :: compiler + type(error_t), allocatable, intent(out) :: error + + !> Cleanup + call destroy(this) + + !> Not implemented yet + call fatal_error(error,'stdlib not supported yet') + +end subroutine init_stdlib + ! Resolve metapackage dependencies into the model subroutine resolve(self,model,error) class(metapackage_t), intent(in) :: self From 0bcad127b27bf44d63804623a091131350975e5e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 08:55:43 +0200 Subject: [PATCH 163/799] metapackage_t: add support for dependencies/dev-dependencies --- src/fpm_meta.f90 | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 21844a5a59..7993b85a53 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -26,10 +26,12 @@ module fpm_meta !> Type for describing a source file type, public :: metapackage_t - logical :: has_link_libraries = .false. - logical :: has_link_flags = .false. - logical :: has_build_flags = .false. - logical :: has_include_dirs = .false. + logical :: has_link_libraries = .false. + logical :: has_link_flags = .false. + logical :: has_build_flags = .false. + logical :: has_include_dirs = .false. + logical :: has_dependencies = .false. + logical :: has_dev_depenencies = .false. !> List of compiler flags and options to be added type(string_t) :: flags @@ -37,6 +39,12 @@ module fpm_meta type(string_t), allocatable :: link_dirs(:) type(string_t), allocatable :: link_libs(:) + !> List of Dependency meta data + type(dependency_config_t), allocatable :: dependency(:) + + !> List of Development dependency meta data + type(dependency_config_t), allocatable :: dev_dependency(:) + contains !> Clean metapackage structure @@ -58,15 +66,19 @@ module fpm_meta elemental subroutine destroy(this) class(metapackage_t), intent(inout) :: this - this%has_link_libraries = .false. - this%has_link_flags = .false. - this%has_build_flags = .false. - this%has_include_dirs = .false. + this%has_link_libraries = .false. + this%has_link_flags = .false. + this%has_build_flags = .false. + this%has_include_dirs = .false. + this%has_dependencies = .false. + this%has_dev_depenencies = .false. if (allocated(this%flags%s)) deallocate(this%flags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) if (allocated(this%link_dirs)) deallocate(this%link_dirs) if (allocated(this%link_libs)) deallocate(this%link_libs) + if (allocated(this%dependency)) deallocate(this%dependency) + if (allocated(this%dev_dependency)) deallocate(this%dev_dependency) end subroutine destroy From 2f377b186799c6405ca46fec513012e013576a68 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 08:56:45 +0200 Subject: [PATCH 164/799] fix use --- src/fpm_meta.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 7993b85a53..a7f5a3ad98 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -17,6 +17,7 @@ module fpm_meta use fpm_error, only: error_t, fatal_error, syntax_error use fpm_compiler use fpm_model +use fpm_manifest_dependency, only: dependency_config_t implicit none private From 3cf0000d36a976394576cb01776c9dbcba06f5e3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 08:59:16 +0200 Subject: [PATCH 165/799] metapackage dependencies are all dev-dependencies --- src/fpm_meta.f90 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index a7f5a3ad98..4569f95e5b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -32,7 +32,6 @@ module fpm_meta logical :: has_build_flags = .false. logical :: has_include_dirs = .false. logical :: has_dependencies = .false. - logical :: has_dev_depenencies = .false. !> List of compiler flags and options to be added type(string_t) :: flags @@ -40,12 +39,10 @@ module fpm_meta type(string_t), allocatable :: link_dirs(:) type(string_t), allocatable :: link_libs(:) - !> List of Dependency meta data + !> List of Development dependency meta data. + !> Metapackage dependencies are never exported from the model type(dependency_config_t), allocatable :: dependency(:) - !> List of Development dependency meta data - type(dependency_config_t), allocatable :: dev_dependency(:) - contains !> Clean metapackage structure @@ -72,14 +69,12 @@ elemental subroutine destroy(this) this%has_build_flags = .false. this%has_include_dirs = .false. this%has_dependencies = .false. - this%has_dev_depenencies = .false. if (allocated(this%flags%s)) deallocate(this%flags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) if (allocated(this%link_dirs)) deallocate(this%link_dirs) if (allocated(this%link_libs)) deallocate(this%link_libs) if (allocated(this%dependency)) deallocate(this%dependency) - if (allocated(this%dev_dependency)) deallocate(this%dev_dependency) end subroutine destroy From 58dff6c6878797c26a84401b44b1ce43ae132048 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 09:18:21 +0200 Subject: [PATCH 166/799] add stdlib metapackage dependencies --- src/fpm_meta.f90 | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 4569f95e5b..3ab635c616 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -18,6 +18,8 @@ module fpm_meta use fpm_compiler use fpm_model use fpm_manifest_dependency, only: dependency_config_t +use fpm_git, only : git_target_branch + implicit none private @@ -54,8 +56,6 @@ module fpm_meta !> Add metapackage dependencies to the model procedure :: resolve - - end type metapackage_t contains @@ -158,8 +158,26 @@ subroutine init_stdlib(this,compiler,error) !> Cleanup call destroy(this) - !> Not implemented yet - call fatal_error(error,'stdlib not supported yet') + !> Stdlib is queried as a dependency from the official repository + this%has_dependencies = .true. + + allocate(this%dependency(2)) + + !> 1) Test-drive + this%dependency(1)%name = "test-drive" + this%dependency(1)%git = git_target_branch("https://github.com/fortran-lang/test-drive","v0.4.0") + if (.not.allocated(this%dependency(1)%git)) then + call fatal_error(error,'cannot initialize test-drive git dependency for stdlib metapackage') + return + end if + + !> 2) stdlib + this%dependency(2)%name = "stdlib" + this%dependency(2)%git = git_target_branch("https://github.com/fortran-lang/stdlib","stdlib-fpm") + if (.not.allocated(this%dependency(2)%git)) then + call fatal_error(error,'cannot initialize git repo dependency for stdlib metapackage') + return + end if end subroutine init_stdlib @@ -188,6 +206,11 @@ subroutine resolve(self,model,error) model%include_dirs = [model%include_dirs,self%link_dirs] end if + ! Add dependencies + if (self%has_dependencies) then + call model%deps%add(self%dependency, error) + endif + end subroutine resolve ! Add named metapackage dependency to the model From 1ee338e2f9f9c82183d0406220de6f629215b7e0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 09:32:47 +0200 Subject: [PATCH 167/799] compiler flags: refactor into a subroutine --- src/fpm.f90 | 57 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 060abd5b38..c21c1a42f7 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -45,7 +45,7 @@ subroutine build_model(model, settings, package, error) integer :: i, j type(package_config_t) :: dependency - character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags + character(len=:), allocatable :: manifest, lib_dir character(len=:), allocatable :: version logical :: has_cpp @@ -60,6 +60,7 @@ subroutine build_model(model, settings, package, error) call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml")) call model%deps%add(package, error) + if (allocated(error)) return ! Update dependencies where needed @@ -76,27 +77,14 @@ subroutine build_model(model, settings, package, error) call new_archiver(model%archiver, settings%archiver, & & echo=settings%verbose, verbose=settings%verbose) - if (settings%flag == '') then - flags = model%compiler%get_default_flags(settings%profile == "release") - else - flags = settings%flag - select case(settings%profile) - case("release", "debug") - flags = flags // model%compiler%get_default_flags(settings%profile == "release") - end select - end if - - cflags = trim(settings%cflag) - cxxflags = trim(settings%cxxflag) - ldflags = trim(settings%ldflag) - if (model%compiler%is_unknown()) then write(*, '(*(a:,1x))') & "", "Unknown compiler", model%compiler%fc, "requested!", & "Defaults for this compiler might be incorrect" end if - model%build_prefix = join_path("build", basename(model%compiler%fc)) + call new_compiler_flags(model,settings) + model%build_prefix = join_path("build", basename(model%compiler%fc)) model%include_tests = settings%build_tests model%enforce_module_names = package%build%module_naming model%module_prefix = package%build%module_prefix @@ -170,18 +158,15 @@ subroutine build_model(model, settings, package, error) end do if (allocated(error)) return - if (has_cpp) call set_cpp_preprocessor_flags(model%compiler%id, flags) - model%fortran_compile_flags = flags - model%c_compile_flags = cflags - model%cxx_compile_flags = cxxflags - model%link_flags = ldflags + ! Add optional flags + if (has_cpp) call set_cpp_preprocessor_flags(model%compiler%id, model%fortran_compile_flags) ! Build and resolve metapackage dependencies if (package%build%openmp) call add_metapackage(model,"openmp",error) if (allocated(error)) return ! Stdlib is available but not implemented yet - if (package%build%stdlib) call fatal_error(error,"stdlib is not implemented yet") + if (package%build%stdlib) call add_metapackage(model,"stdlib",error) if (allocated(error)) return ! Add sources from executable directories @@ -267,6 +252,34 @@ subroutine build_model(model, settings, package, error) end if end subroutine build_model +!> Initialize model compiler flags +subroutine new_compiler_flags(model,settings) + type(fpm_model_t), intent(inout) :: model + type(fpm_build_settings), intent(in) :: settings + + character(len=:), allocatable :: flags, cflags, cxxflags, ldflags + + if (settings%flag == '') then + flags = model%compiler%get_default_flags(settings%profile == "release") + else + flags = settings%flag + select case(settings%profile) + case("release", "debug") + flags = flags // model%compiler%get_default_flags(settings%profile == "release") + end select + end if + + cflags = trim(settings%cflag) + cxxflags = trim(settings%cxxflag) + ldflags = trim(settings%ldflag) + + model%fortran_compile_flags = flags + model%c_compile_flags = cflags + model%cxx_compile_flags = cxxflags + model%link_flags = ldflags + +end subroutine new_compiler_flags + ! Check for duplicate modules subroutine check_modules_for_duplicates(model, duplicates_found) type(fpm_model_t), intent(in) :: model From ebc815657251955c80ba60c33658afed10b33054 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 09:35:58 +0200 Subject: [PATCH 168/799] package dependencies: reorganize code so they're done at the same time --- src/fpm.f90 | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index c21c1a42f7..3e38321f6e 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -58,15 +58,6 @@ subroutine build_model(model, settings, package, error) allocate(model%link_libraries(0)) allocate(model%external_modules(0)) - call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml")) - call model%deps%add(package, error) - - if (allocated(error)) return - - ! Update dependencies where needed - call model%deps%update(error) - if (allocated(error)) return - ! build/ directory should now exist if (.not.exists("build/.gitignore")) then call filewrite(join_path("build", ".gitignore"),["*"]) @@ -89,6 +80,23 @@ subroutine build_model(model, settings, package, error) model%enforce_module_names = package%build%module_naming model%module_prefix = package%build%module_prefix + ! Create dependencies + call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml")) + call model%deps%add(package, error) + if (allocated(error)) return + + ! Build and resolve metapackage dependencies and flags + if (package%build%openmp) call add_metapackage(model,"openmp",error) + if (allocated(error)) return + + ! Stdlib is available but not implemented yet + if (package%build%stdlib) call add_metapackage(model,"stdlib",error) + if (allocated(error)) return + + ! Update dependencies where needed + call model%deps%update(error) + if (allocated(error)) return + allocate(model%packages(model%deps%ndep)) has_cpp = .false. @@ -161,14 +169,6 @@ subroutine build_model(model, settings, package, error) ! Add optional flags if (has_cpp) call set_cpp_preprocessor_flags(model%compiler%id, model%fortran_compile_flags) - ! Build and resolve metapackage dependencies - if (package%build%openmp) call add_metapackage(model,"openmp",error) - if (allocated(error)) return - - ! Stdlib is available but not implemented yet - if (package%build%stdlib) call add_metapackage(model,"stdlib",error) - if (allocated(error)) return - ! Add sources from executable directories if (is_dir('app') .and. package%build%auto_executables) then call add_sources_from_dir(model%packages(1)%sources,'app', FPM_SCOPE_APP, & From 53d4ae7ec352b8d07bb0698374e1c225f0b1612f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 09:39:15 +0200 Subject: [PATCH 169/799] fix: move down .gitignore --- src/fpm.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 3e38321f6e..fbe71f53bc 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -58,11 +58,6 @@ subroutine build_model(model, settings, package, error) allocate(model%link_libraries(0)) allocate(model%external_modules(0)) - ! build/ directory should now exist - if (.not.exists("build/.gitignore")) then - call filewrite(join_path("build", ".gitignore"),["*"]) - end if - call new_compiler(model%compiler, settings%compiler, settings%c_compiler, & & settings%cxx_compiler, echo=settings%verbose, verbose=settings%verbose) call new_archiver(model%archiver, settings%archiver, & @@ -97,6 +92,11 @@ subroutine build_model(model, settings, package, error) call model%deps%update(error) if (allocated(error)) return + ! build/ directory should now exist + if (.not.exists("build/.gitignore")) then + call filewrite(join_path("build", ".gitignore"),["*"]) + end if + allocate(model%packages(model%deps%ndep)) has_cpp = .false. From bdfd25a88bf336f7dc1373f2fadcfcee1cb051f8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 10:21:41 +0200 Subject: [PATCH 170/799] move all metapackages resolution to `fpm_meta.f90`, cleanup `fpm.f90` --- src/fpm.f90 | 18 +++++----- src/fpm_meta.f90 | 90 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 19 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index fbe71f53bc..a6bffdaba2 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -21,7 +21,7 @@ module fpm resolve_target_linking, build_target_t, build_target_ptr, & FPM_TARGET_EXECUTABLE, FPM_TARGET_ARCHIVE use fpm_manifest, only : get_package_data, package_config_t -use fpm_meta, only : add_metapackage +use fpm_meta, only : resolve_metapackages use fpm_error, only : error_t, fatal_error, fpm_stop use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, & & stdout=>output_unit, & @@ -40,7 +40,7 @@ subroutine build_model(model, settings, package, error) ! type(fpm_model_t), intent(out) :: model type(fpm_build_settings), intent(in) :: settings - type(package_config_t), intent(in) :: package + type(package_config_t), intent(inout) :: package type(error_t), allocatable, intent(out) :: error integer :: i, j @@ -75,17 +75,15 @@ subroutine build_model(model, settings, package, error) model%enforce_module_names = package%build%module_naming model%module_prefix = package%build%module_prefix - ! Create dependencies - call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml")) - call model%deps%add(package, error) + ! Resolve meta-dependencies into the package and the model + call resolve_metapackages(model,package,error) if (allocated(error)) return - ! Build and resolve metapackage dependencies and flags - if (package%build%openmp) call add_metapackage(model,"openmp",error) - if (allocated(error)) return + ! Create dependencies + call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml")) - ! Stdlib is available but not implemented yet - if (package%build%stdlib) call add_metapackage(model,"stdlib",error) + ! Build and resolve model dependencies + call model%deps%add(package, error) if (allocated(error)) return ! Update dependencies where needed diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 3ab635c616..e03896800d 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -19,12 +19,13 @@ module fpm_meta use fpm_model use fpm_manifest_dependency, only: dependency_config_t use fpm_git, only : git_target_branch +use fpm_manifest, only: package_config_t implicit none private -public :: add_metapackage +public :: resolve_metapackages !> Type for describing a source file type, public :: metapackage_t @@ -54,10 +55,16 @@ module fpm_meta procedure :: new => init_from_name !> Add metapackage dependencies to the model - procedure :: resolve + procedure, private :: resolve_model + procedure, private :: resolve_package_config + generic :: resolve => resolve_model,resolve_package_config end type metapackage_t +interface resolve_metapackages + module procedure resolve_metapackage_model +end interface resolve_metapackages + contains !> Clean the metapackage structure @@ -182,7 +189,7 @@ subroutine init_stdlib(this,compiler,error) end subroutine init_stdlib ! Resolve metapackage dependencies into the model -subroutine resolve(self,model,error) +subroutine resolve_model(self,model,error) class(metapackage_t), intent(in) :: self type(fpm_model_t), intent(inout) :: model type(error_t), allocatable, intent(out) :: error @@ -206,15 +213,29 @@ subroutine resolve(self,model,error) model%include_dirs = [model%include_dirs,self%link_dirs] end if - ! Add dependencies + ! Dependencies are resolved in the package config + +end subroutine resolve_model + +subroutine resolve_package_config(self,package,error) + class(metapackage_t), intent(in) :: self + type(package_config_t), intent(inout) :: package + type(error_t), allocatable, intent(out) :: error + + ! All metapackage dependencies are added as full dependencies, + ! as upstream projects will not otherwise compile without them if (self%has_dependencies) then - call model%deps%add(self%dependency, error) - endif + if (allocated(package%dependency)) then + package%dependency = [package%dependency,self%dependency] + else + package%dependency = self%dependency + end if + end if -end subroutine resolve +end subroutine resolve_package_config ! Add named metapackage dependency to the model -subroutine add_metapackage(model,name,error) +subroutine add_metapackage_model(model,name,error) type(fpm_model_t), intent(inout) :: model character(*), intent(in) :: name type(error_t), allocatable, intent(out) :: error @@ -229,6 +250,57 @@ subroutine add_metapackage(model,name,error) call meta%resolve(model,error) if (allocated(error)) return -end subroutine add_metapackage +end subroutine add_metapackage_model + +! Add named metapackage dependency to the model +subroutine add_metapackage_config(package,compiler,name,error) + type(package_config_t), intent(inout) :: package + type(compiler_t), intent(in) :: compiler + character(*), intent(in) :: name + type(error_t), allocatable, intent(out) :: error + + type(metapackage_t) :: meta + + !> Init metapackage + call meta%new(name,compiler,error) + if (allocated(error)) return + + !> Add it to the model + call meta%resolve(package,error) + if (allocated(error)) return + +end subroutine add_metapackage_config + +!> Resolve all metapackages into the package config +subroutine resolve_metapackage_model(model,package,error) + type(fpm_model_t), intent(inout) :: model + type(package_config_t), intent(inout) :: package + type(error_t), allocatable, intent(out) :: error + + ! Dependencies are added to the package config, so they're properly resolved + ! into the dependency tree later. + ! Flags are added to the model (whose compiler needs to be already initialized) + if (model%compiler%is_unknown()) then + call fatal_error(error,"compiler not initialized: cannot build metapackages") + return + end if + + ! OpenMP + if (package%build%openmp) then + call add_metapackage_model(model,"openmp",error) + if (allocated(error)) return + call add_metapackage_config(package,model%compiler,"openmp",error) + if (allocated(error)) return + endif + + ! stdlib + if (package%build%stdlib) then + call add_metapackage_model(model,"stdlib",error) + if (allocated(error)) return + call add_metapackage_config(package,model%compiler,"stdlib",error) + if (allocated(error)) return + endif + +end subroutine resolve_metapackage_model end module fpm_meta From 029fee3b440dd94e564cca38c2177617696fa25a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 10:38:42 +0200 Subject: [PATCH 171/799] warn users for simultaneous openMP+stdlib dependencies --- src/fpm_meta.f90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e03896800d..a817a36cb4 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -20,6 +20,7 @@ module fpm_meta use fpm_manifest_dependency, only: dependency_config_t use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t +use iso_fortran_env, only: stdout => output_unit implicit none @@ -301,6 +302,11 @@ subroutine resolve_metapackage_model(model,package,error) if (allocated(error)) return endif + ! Stdlib is not 100% thread safe. print a warning to the user + if (package%build%stdlib .and. package%build%openmp) then + write(stdout,'(a)')' both openmp and stdlib requested: some functions may not be thread-safe!' + end if + end subroutine resolve_metapackage_model end module fpm_meta From b504db8cbcf35d0e0e0f1afb82fa63f4c25be0f4 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 11:07:10 +0200 Subject: [PATCH 172/799] add test and CI --- ci/run_tests.sh | 13 +++++++++- example_packages/metapackage_stdlib/README.md | 4 ++++ .../metapackage_stdlib/app/main.f90 | 24 +++++++++++++++++++ example_packages/metapackage_stdlib/fpm.toml | 13 ++++++++++ .../src/metapackage_stdlib.f90 | 10 ++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 example_packages/metapackage_stdlib/README.md create mode 100644 example_packages/metapackage_stdlib/app/main.f90 create mode 100644 example_packages/metapackage_stdlib/fpm.toml create mode 100644 example_packages/metapackage_stdlib/src/metapackage_stdlib.f90 diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 2f46b0f0de..a3c2c23218 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -195,7 +195,18 @@ popd # Test metapackages pushd metapackage_openmp -"$fpm" build +EXIT_CODE=0 +"$fpm" build || EXIT_CODE=$? +test $EXIT_CODE -eq 0 +EXIT_CODE=0 +"$fpm" run || EXIT_CODE=$? +test $EXIT_CODE -eq 0 +popd + +pushd metapackage_stdlib +EXIT_CODE=0 +"$fpm" build || EXIT_CODE=$? +test $EXIT_CODE -eq 0 EXIT_CODE=0 "$fpm" run || EXIT_CODE=$? test $EXIT_CODE -eq 0 diff --git a/example_packages/metapackage_stdlib/README.md b/example_packages/metapackage_stdlib/README.md new file mode 100644 index 0000000000..11eddc7462 --- /dev/null +++ b/example_packages/metapackage_stdlib/README.md @@ -0,0 +1,4 @@ +# test_stdlib +This test program generates a real [1,2,3,4,5] array using stdlib. +stdlib math and kinds modules are invoked; so this program cannot be built if stdlib is not +properly built and linked. stdlib tests are not run in this program. diff --git a/example_packages/metapackage_stdlib/app/main.f90 b/example_packages/metapackage_stdlib/app/main.f90 new file mode 100644 index 0000000000..30630e90a6 --- /dev/null +++ b/example_packages/metapackage_stdlib/app/main.f90 @@ -0,0 +1,24 @@ +! fortran-lang stdlib test case +! This test program will only run if stdlib is properly built and linked to this project. +program test_stdlib_metapackage + + ! These USEs would not be possible if stdlib is not found + use stdlib_kinds, only: int32, int64, dp, sp + use stdlib_math + implicit none + + real(dp), allocatable :: indices(:) + + indices = linspace(1.0_dp,5.0_dp,5) + + if (.not.allocated(indices)) then + stop 1 + elseif (size(indices)/=5) then + stop 2 + elseif (any(nint(indices)/=[1,2,3,4,5])) then + stop 3 + else + stop 0 + endif + +end program test_stdlib_metapackage diff --git a/example_packages/metapackage_stdlib/fpm.toml b/example_packages/metapackage_stdlib/fpm.toml new file mode 100644 index 0000000000..50faf70022 --- /dev/null +++ b/example_packages/metapackage_stdlib/fpm.toml @@ -0,0 +1,13 @@ +name = "test_stdlib" +version = "0.1.0" +license = "license" +author = "Federico Perini" +maintainer = "federico.perini@hello.world" +copyright = "Copyright 2023, Federico Perini and the fpm maintainers" +[build] +auto-executables = true +auto-tests = true +auto-examples = true +stdlib = true +[install] +library = false diff --git a/example_packages/metapackage_stdlib/src/metapackage_stdlib.f90 b/example_packages/metapackage_stdlib/src/metapackage_stdlib.f90 new file mode 100644 index 0000000000..4f041d6d7d --- /dev/null +++ b/example_packages/metapackage_stdlib/src/metapackage_stdlib.f90 @@ -0,0 +1,10 @@ +module metapackage_stdlib + implicit none + private + + public :: say_hello +contains + subroutine say_hello + print *, "Hello, metapackage_stdlib!" + end subroutine say_hello +end module metapackage_stdlib From cfd29403bd333649adcc3d85d49b3811bd19b9fa Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 11:55:16 +0200 Subject: [PATCH 173/799] test manual gcc-9 tweak --- .github/workflows/CI.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 182891dd16..000b97edf8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -51,6 +51,12 @@ jobs: ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran which gfortran-${GCC_V} which gfortran + # Backport gfortran shared libraries to version 9 folder (hardcoded in fpm 0.3.0 executable) + ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib + ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib + ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib + +/usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib - name: Install GFortran Linux if: contains(matrix.os, 'ubuntu') From bd091d38427796699c260d7e7351d7cef7012add Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 11:58:49 +0200 Subject: [PATCH 174/799] typo --- .github/workflows/CI.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 000b97edf8..9fa6404e99 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -56,8 +56,6 @@ jobs: ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib -/usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib - - name: Install GFortran Linux if: contains(matrix.os, 'ubuntu') run: | From 19dc35efa5c630d2916f260c5cc4d1c93be5ee92 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 12:02:28 +0200 Subject: [PATCH 175/799] manually create dirs --- .github/workflows/CI.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9fa6404e99..afd44cec7d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -52,6 +52,11 @@ jobs: which gfortran-${GCC_V} which gfortran # Backport gfortran shared libraries to version 9 folder (hardcoded in fpm 0.3.0 executable) + mkdir /usr/local/opt/gcc@9 + mkdir /usr/local/opt/gcc@9/lib + mkdir /usr/local/opt/gcc@9/lib/gcc + mkdir /usr/local/opt/gcc@9/lib/gcc/9 + mkdir /usr/local/lib/gcc/9 ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib From 5a74d581cbdfa8e86f183d35e54d707bf076b865 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 12:12:08 +0200 Subject: [PATCH 176/799] document change --- .github/workflows/CI.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index afd44cec7d..297fe11514 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -51,7 +51,9 @@ jobs: ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran which gfortran-${GCC_V} which gfortran - # Backport gfortran shared libraries to version 9 folder (hardcoded in fpm 0.3.0 executable) + # Backport gfortran shared libraries to version 9 folder. This is necessary because all macOS releases of fpm + # have these paths hardcoded in the executable (no PIC?). As the gcc ABIs have not changed from 9 to 10, we + # can just create symbolic links for now. This can be removed when an updated fpm release is built with gcc-10 mkdir /usr/local/opt/gcc@9 mkdir /usr/local/opt/gcc@9/lib mkdir /usr/local/opt/gcc@9/lib/gcc From c8a7ce8b5ec4f71c5cc95fc68743a52bc264fb22 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 30 Mar 2023 14:17:53 +0200 Subject: [PATCH 177/799] restore CI --- .github/workflows/CI.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 182891dd16..297fe11514 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -51,6 +51,17 @@ jobs: ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran which gfortran-${GCC_V} which gfortran + # Backport gfortran shared libraries to version 9 folder. This is necessary because all macOS releases of fpm + # have these paths hardcoded in the executable (no PIC?). As the gcc ABIs have not changed from 9 to 10, we + # can just create symbolic links for now. This can be removed when an updated fpm release is built with gcc-10 + mkdir /usr/local/opt/gcc@9 + mkdir /usr/local/opt/gcc@9/lib + mkdir /usr/local/opt/gcc@9/lib/gcc + mkdir /usr/local/opt/gcc@9/lib/gcc/9 + mkdir /usr/local/lib/gcc/9 + ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib + ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib + ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib - name: Install GFortran Linux if: contains(matrix.os, 'ubuntu') From 6c065ce0519b5325dee7103c1e567a92c78df1f4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 14:30:04 +0700 Subject: [PATCH 178/799] Updae jonquil --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 2a02e53aaa..13fc50451f 100644 --- a/fpm.toml +++ b/fpm.toml @@ -16,7 +16,7 @@ rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" [dependencies.jonquil] git = "https://github.com/toml-f/jonquil" -rev = "93354799980556023442b2307010c600370af097" +rev = "05d30818bb12fb877226ce284b9a3a41b971a889" [[test]] name = "cli-test" From 55d94b0eb5685b2214ea7c2f2039ba7e9c7bf418 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Sun, 2 Apr 2023 09:30:16 +0200 Subject: [PATCH 179/799] Update TOML Fortran to latest version (#862) --- fpm.toml | 11 ++++------- src/fpm/cmd/new.f90 | 23 ++++++++++------------- src/fpm/dependency.f90 | 11 ++++------- src/fpm/toml.f90 | 8 ++++---- 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/fpm.toml b/fpm.toml index 9c694cab41..ec70e34043 100644 --- a/fpm.toml +++ b/fpm.toml @@ -6,13 +6,10 @@ maintainer = "" copyright = "2020 fpm contributors" [dependencies] -[dependencies.toml-f] -git = "https://github.com/toml-f/toml-f" -rev = "aee54c5a480d623af99828c76df0447a15ce90dc" - -[dependencies.M_CLI2] -git = "https://github.com/urbanjost/M_CLI2.git" -rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" +toml-f.git = "https://github.com/toml-f/toml-f" +toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" +M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" +M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" [[test]] name = "cli-test" diff --git a/src/fpm/cmd/new.f90 b/src/fpm/cmd/new.f90 index bed0980553..4d715343cb 100644 --- a/src/fpm/cmd/new.f90 +++ b/src/fpm/cmd/new.f90 @@ -627,17 +627,17 @@ end function git_metadata subroutine create_verified_basic_manifest(filename) !> create a basic but verified default manifest file -use fpm_toml, only : toml_table, toml_serializer, set_value +use fpm_toml, only : toml_table, toml_serialize, set_value use fpm_manifest_package, only : package_config_t, new_package use fpm_error, only : error_t implicit none character(len=*),intent(in) :: filename type(toml_table) :: table - type(toml_serializer) :: ser type(package_config_t) :: package type(error_t), allocatable :: error integer :: lun character(len=8) :: date + character(:), allocatable :: output if(exists(filename))then write(stderr,'(*(g0,1x))')' ',filename,& @@ -647,7 +647,6 @@ subroutine create_verified_basic_manifest(filename) !> get date to put into metadata in manifest file "fpm.toml" call date_and_time(DATE=date) table = toml_table() - ser = toml_serializer() call fileopen(filename,lun) ! fileopen stops on error call set_value(table, "name", BNAME) @@ -660,11 +659,11 @@ subroutine create_verified_basic_manifest(filename) ! ... call new_package(package, table, error=error) if (allocated(error)) call fpm_stop( 3,'') + output = toml_serialize(table) if(settings%verbose)then - call table%accept(ser) + print '(a)', output endif - ser%unit=lun - call table%accept(ser) + write(lun, '(a)') output call fileclose(lun) ! fileopen stops on error end subroutine create_verified_basic_manifest @@ -673,27 +672,25 @@ end subroutine create_verified_basic_manifest subroutine validate_toml_data(input) !> verify a string array is a valid fpm.toml file ! -use tomlf, only : toml_parse -use fpm_toml, only : toml_table, toml_serializer +use tomlf, only : toml_load +use fpm_toml, only : toml_table, toml_serialize implicit none character(kind=tfc,len=:),intent(in),allocatable :: input(:) character(len=1), parameter :: nl = new_line('a') type(toml_table), allocatable :: table character(kind=tfc, len=:), allocatable :: joined_string -type(toml_serializer) :: ser ! you have to add a newline character by using the intrinsic ! function `new_line("a")` to get the lines processed correctly. joined_string = join(input,right=nl) if (allocated(table)) deallocate(table) -call toml_parse(table, joined_string) +call toml_load(table, joined_string) if (allocated(table)) then if(settings%verbose)then ! If the TOML file is successfully parsed the table will be allocated and - ! can be written to the standard output by passing the `toml_serializer` - ! as visitor to the table. - call table%accept(ser) + ! can be written by `toml_serialize` to the standard output + print '(a)', toml_serialize(table) endif call table%destroy endif diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index ef2993563b..a314485313 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -64,8 +64,8 @@ module fpm_dependency get_package_data use fpm_manifest_dependency, only: manifest_has_changed use fpm_strings, only : string_t, operator(.in.) - use fpm_toml, only : toml_table, toml_key, toml_error, toml_serializer, & - toml_parse, get_value, set_value, add_table + use fpm_toml, only : toml_table, toml_key, toml_error, toml_serialize, & + toml_load, get_value, set_value, add_table use fpm_versioning, only : version_t, new_version, char implicit none private @@ -720,7 +720,7 @@ subroutine load_from_unit(self, unit, error) type(toml_error), allocatable :: parse_error type(toml_table), allocatable :: table - call toml_parse(table, unit, parse_error) + call toml_load(table, unit, error=parse_error) if (allocated(parse_error)) then allocate(error) @@ -830,14 +830,11 @@ subroutine dump_to_unit(self, unit, error) type(error_t), allocatable, intent(out) :: error type(toml_table) :: table - type(toml_serializer) :: ser table = toml_table() - ser = toml_serializer(unit) - call self%dump(table, error) - call table%accept(ser) + write(unit, '(a)') toml_serialize(table) end subroutine dump_to_unit diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 85560ba156..3c1dfaa175 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -16,15 +16,15 @@ module fpm_toml use fpm_error, only : error_t, fatal_error, file_not_found_error use fpm_strings, only : string_t use tomlf, only : toml_table, toml_array, toml_key, toml_stat, get_value, & - & set_value, toml_parse, toml_error, new_table, add_table, add_array, & - & toml_serializer, len + & set_value, toml_load, toml_error, new_table, add_table, add_array, & + & toml_serialize, len implicit none private public :: read_package_file public :: toml_table, toml_array, toml_key, toml_stat, get_value, set_value, get_list public :: new_table, add_table, add_array, len - public :: toml_error, toml_serializer, toml_parse + public :: toml_error, toml_serialize, toml_load contains @@ -54,7 +54,7 @@ subroutine read_package_file(table, manifest, error) end if open(file=manifest, newunit=unit) - call toml_parse(table, unit, parse_error) + call toml_load(table, unit, error=parse_error) close(unit) if (allocated(parse_error)) then From 225a4620ced418f21277115066682e301ff450dc Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 18:11:06 +0700 Subject: [PATCH 180/799] Use consistent toml style --- fpm.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fpm.toml b/fpm.toml index 127ecd6f7e..413c21b817 100644 --- a/fpm.toml +++ b/fpm.toml @@ -10,10 +10,8 @@ toml-f.git = "https://github.com/toml-f/toml-f" toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" - -[dependencies.jonquil] -git = "https://github.com/toml-f/jonquil" -rev = "05d30818bb12fb877226ce284b9a3a41b971a889" +jonquil.git = "https://github.com/toml-f/jonquil" +jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" [[test]] name = "cli-test" From 81ba0bb7adc4852bb94da787959b5d4643225447 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 18:44:07 +0700 Subject: [PATCH 181/799] Change registry base url --- src/fpm_settings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 927b83f6df..cc53df2f7d 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -10,7 +10,7 @@ module fpm_settings private public :: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url - character(*), parameter :: official_registry_base_url = 'https://minhdao.pythonanywhere.com' + character(*), parameter :: official_registry_base_url = 'https://fpm-registry.onrender.com' type :: fpm_global_settings !> Path to the global config file excluding the file name. From e1afc82b438729d64e07955978a69e15330976b1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 18:59:42 +0700 Subject: [PATCH 182/799] Use longer names --- app/main.f90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/main.f90 b/app/main.f90 index 69ecbe2b55..10f75b8318 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -99,20 +99,20 @@ function has_manifest(dir) has_manifest = exists(join_path(dir, "fpm.toml")) end function has_manifest - subroutine handle_error(e) - type(error_t), optional, intent(in) :: e - if (present(e)) then - write (error_unit, '("[Error]", 1x, a)') e%message + subroutine handle_error(error_) + type(error_t), optional, intent(in) :: error_ + if (present(error_)) then + write (error_unit, '("[Error]", 1x, a)') error_%message stop 1 end if end subroutine handle_error !> Save access to working directory in settings, in case setting have not been allocated - subroutine get_working_dir(settings, w_dir) + subroutine get_working_dir(settings, working_dir_) class(fpm_cmd_settings), optional, intent(in) :: settings - character(len=:), allocatable, intent(out) :: w_dir + character(len=:), allocatable, intent(out) :: working_dir_ if (present(settings)) then - w_dir = settings%working_dir + working_dir_ = settings%working_dir end if end subroutine get_working_dir From acf4139e6f82938640ade4b14181cdac806fef8a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 19:09:25 +0700 Subject: [PATCH 183/799] Use more informative variable names --- src/fpm/dependency.f90 | 18 +++++++++--------- src/fpm/downloader.f90 | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index bc8b1cb418..251b575ff0 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -614,7 +614,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade !> Downloader instance. class(downloader_t), optional, intent(in) :: downloader_ - character(:), allocatable :: cache_path, target_url, tmp_file, tmp_path + character(:), allocatable :: cache_path, target_url, tmp_pkg_data_path, tmp_pkg_data_file type(version_t) :: version integer :: stat, unit type(json_object) :: json @@ -644,17 +644,17 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end if ! Define location of the temporary folder and file. - tmp_path = join_path(global_settings%path_to_config_folder, 'tmp') - tmp_file = join_path(tmp_path, 'package_data.tmp') - if (.not. exists(tmp_path)) call mkdir(tmp_path) - open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) + tmp_pkg_data_path = join_path(global_settings%path_to_config_folder, 'tmp') + tmp_pkg_data_file = join_path(tmp_pkg_data_path, 'package_data.tmp') + if (.not. exists(tmp_pkg_data_path)) call mkdir(tmp_pkg_data_path) + open (newunit=unit, file=tmp_pkg_data_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if ! Include namespace and package name in the target url and download package data. target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name - call downloader%get_pkg_data(target_url, self%requested_version, tmp_file, json, error) + call downloader%get_pkg_data(target_url, self%requested_version, tmp_pkg_data_file, json, error) close (unit, status='delete') if (allocated(error)) return @@ -663,7 +663,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (allocated(error)) return ! Open new tmp file for downloading the actual package. - open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) + open (newunit=unit, file=tmp_pkg_data_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if @@ -675,13 +675,13 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade call mkdir(cache_path) print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." - call downloader%get_file(target_url, tmp_file, error) + call downloader%get_file(target_url, tmp_pkg_data_file, error) if (allocated(error)) then close (unit, status='delete'); return end if ! Unpack the downloaded package to the final location. - call downloader%unpack(tmp_file, cache_path, error) + call downloader%unpack(tmp_pkg_data_file, cache_path, error) close (unit, status='delete') if (allocated(error)) return end if diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 34eb58b70d..92fbdced79 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -18,10 +18,10 @@ module fpm_downloader contains !> Perform an http get request and save output to file. - subroutine get_pkg_data(url, version, tmp_file, json, error) + subroutine get_pkg_data(url, version, tmp_pkg_data_file, json, error) character(*), intent(in) :: url type(version_t), allocatable, intent(in) :: version - character(*), intent(in) :: tmp_file + character(*), intent(in) :: tmp_pkg_data_file type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error @@ -31,14 +31,14 @@ subroutine get_pkg_data(url, version, tmp_file, json, error) if (allocated(version)) then ! Request specific version. - call get_file(url//'/'//version%s(), tmp_file, error) + call get_file(url//'/'//version%s(), tmp_pkg_data_file, error) else ! Request latest version. - call get_file(url, tmp_file, error) + call get_file(url, tmp_pkg_data_file, error) end if if (allocated(error)) return - call json_load(j_value, tmp_file, error=j_error) + call json_load(j_value, tmp_pkg_data_file, error=j_error) if (allocated(j_error)) then allocate (error); call move_alloc(j_error%message, error%message); call json%destroy(); return end if @@ -51,19 +51,19 @@ subroutine get_pkg_data(url, version, tmp_file, json, error) json = ptr end - subroutine get_file(url, tmp_file, error) + subroutine get_file(url, tmp_pkg_data_file, error) character(*), intent(in) :: url - character(*), intent(in) :: tmp_file + character(*), intent(in) :: tmp_pkg_data_file type(error_t), allocatable, intent(out) :: error integer :: stat if (which('curl') /= '') then print *, "Downloading package data from '"//url//"' ..." - call execute_command_line('curl '//url//' -s -o '//tmp_file, exitstat=stat) + call execute_command_line('curl '//url//' -s -o '//tmp_pkg_data_file, exitstat=stat) else if (which('wget') /= '') then print *, "Downloading package data from '"//url//"' ..." - call execute_command_line('wget '//url//' -q -O '//tmp_file, exitstat=stat) + call execute_command_line('wget '//url//' -q -O '//tmp_pkg_data_file, exitstat=stat) else call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return end if From cd9e5c61504a2dc5c45232667c6f8b9e77f3fa9c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 19:25:21 +0700 Subject: [PATCH 184/799] Remove unused import --- src/fpm_command_line.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 7ca7fbc5a6..985d8892a2 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -29,7 +29,7 @@ module fpm_command_line use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified use M_CLI2, only : get_subcommand, CLI_RESPONSE_FILE use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name -use fpm_filesystem, only : basename, canon_path, which, run, join_path +use fpm_filesystem, only : basename, canon_path, which, run use fpm_environment, only : get_command_arguments_quoted use fpm_error, only : fpm_stop, error_t use fpm_os, only : get_current_directory From 29ea77f2fe87a85b3d0c594123902acf6e1e06a5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 19:45:30 +0700 Subject: [PATCH 185/799] Improve variable names again --- src/fpm/dependency.f90 | 20 +++++++------- src/fpm/downloader.f90 | 29 +++++++++++---------- test/fpm_test/test_package_dependencies.f90 | 14 +++++----- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 251b575ff0..7e1a694f28 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -614,7 +614,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade !> Downloader instance. class(downloader_t), optional, intent(in) :: downloader_ - character(:), allocatable :: cache_path, target_url, tmp_pkg_data_path, tmp_pkg_data_file + character(:), allocatable :: cache_path, target_url, tmp_pkg_path, tmp_pkg_file type(version_t) :: version integer :: stat, unit type(json_object) :: json @@ -644,17 +644,17 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end if ! Define location of the temporary folder and file. - tmp_pkg_data_path = join_path(global_settings%path_to_config_folder, 'tmp') - tmp_pkg_data_file = join_path(tmp_pkg_data_path, 'package_data.tmp') - if (.not. exists(tmp_pkg_data_path)) call mkdir(tmp_pkg_data_path) - open (newunit=unit, file=tmp_pkg_data_file, action='readwrite', iostat=stat) + tmp_pkg_path = join_path(global_settings%path_to_config_folder, 'tmp') + tmp_pkg_file = join_path(tmp_pkg_path, 'package_data.tmp') + if (.not. exists(tmp_pkg_path)) call mkdir(tmp_pkg_path) + open (newunit=unit, file=tmp_pkg_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if ! Include namespace and package name in the target url and download package data. target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name - call downloader%get_pkg_data(target_url, self%requested_version, tmp_pkg_data_file, json, error) + call downloader%get_pkg_data(target_url, self%requested_version, tmp_pkg_file, json, error) close (unit, status='delete') if (allocated(error)) return @@ -663,7 +663,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (allocated(error)) return ! Open new tmp file for downloading the actual package. - open (newunit=unit, file=tmp_pkg_data_file, action='readwrite', iostat=stat) + open (newunit=unit, file=tmp_pkg_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if @@ -675,13 +675,13 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade call mkdir(cache_path) print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." - call downloader%get_file(target_url, tmp_pkg_data_file, error) + call downloader%get_file(target_url, tmp_pkg_file, error) if (allocated(error)) then close (unit, status='delete'); return end if ! Unpack the downloaded package to the final location. - call downloader%unpack(tmp_pkg_data_file, cache_path, error) + call downloader%unpack(tmp_pkg_file, cache_path, error) close (unit, status='delete') if (allocated(error)) return end if @@ -1082,7 +1082,7 @@ subroutine dump_to_unit(self, unit, error) table = toml_table() call self%dump(table, error) - write(unit, '(a)') toml_serialize(table) + write (unit, '(a)') toml_serialize(table) end subroutine dump_to_unit diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 92fbdced79..1f631ca0a0 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -18,10 +18,10 @@ module fpm_downloader contains !> Perform an http get request and save output to file. - subroutine get_pkg_data(url, version, tmp_pkg_data_file, json, error) + subroutine get_pkg_data(url, version, tmp_pkg_file, json, error) character(*), intent(in) :: url type(version_t), allocatable, intent(in) :: version - character(*), intent(in) :: tmp_pkg_data_file + character(*), intent(in) :: tmp_pkg_file type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error @@ -31,14 +31,14 @@ subroutine get_pkg_data(url, version, tmp_pkg_data_file, json, error) if (allocated(version)) then ! Request specific version. - call get_file(url//'/'//version%s(), tmp_pkg_data_file, error) + call get_file(url//'/'//version%s(), tmp_pkg_file, error) else ! Request latest version. - call get_file(url, tmp_pkg_data_file, error) + call get_file(url, tmp_pkg_file, error) end if if (allocated(error)) return - call json_load(j_value, tmp_pkg_data_file, error=j_error) + call json_load(j_value, tmp_pkg_file, error=j_error) if (allocated(j_error)) then allocate (error); call move_alloc(j_error%message, error%message); call json%destroy(); return end if @@ -51,19 +51,19 @@ subroutine get_pkg_data(url, version, tmp_pkg_data_file, json, error) json = ptr end - subroutine get_file(url, tmp_pkg_data_file, error) + subroutine get_file(url, tmp_pkg_file, error) character(*), intent(in) :: url - character(*), intent(in) :: tmp_pkg_data_file + character(*), intent(in) :: tmp_pkg_file type(error_t), allocatable, intent(out) :: error integer :: stat if (which('curl') /= '') then print *, "Downloading package data from '"//url//"' ..." - call execute_command_line('curl '//url//' -s -o '//tmp_pkg_data_file, exitstat=stat) + call execute_command_line('curl '//url//' -s -o '//tmp_pkg_file, exitstat=stat) else if (which('wget') /= '') then print *, "Downloading package data from '"//url//"' ..." - call execute_command_line('wget '//url//' -q -O '//tmp_pkg_data_file, exitstat=stat) + call execute_command_line('wget '//url//' -q -O '//tmp_pkg_file, exitstat=stat) else call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return end if @@ -73,8 +73,9 @@ subroutine get_file(url, tmp_pkg_data_file, error) end if end - subroutine unpack(tmp_file, destination, error) - character(*), intent(in) :: tmp_file + !> Unpack a tarball to a destination. + subroutine unpack(tmp_pkg_file, destination, error) + character(*), intent(in) :: tmp_pkg_file character(*), intent(in) :: destination type(error_t), allocatable, intent(out) :: error @@ -84,11 +85,11 @@ subroutine unpack(tmp_file, destination, error) call fatal_error(error, "'tar' not installed."); return end if - print *, "Unpacking '"//tmp_file//"' to '"//destination//"' ..." - call execute_command_line('tar -zxf '//tmp_file//' -C '//destination, exitstat=stat) + print *, "Unpacking '"//tmp_pkg_file//"' to '"//destination//"' ..." + call execute_command_line('tar -zxf '//tmp_pkg_file//' -C '//destination, exitstat=stat) if (stat /= 0) then - call fatal_error(error, "Error unpacking '"//tmp_file//"'."); return + call fatal_error(error, "Error unpacking '"//tmp_pkg_file//"'."); return end if end end diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 817d4bdc63..4b2f5bcfc4 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -1350,10 +1350,10 @@ subroutine setup_global_settings(global_settings, error) global_settings%config_file_name = config_file_name end - subroutine get_pkg_data(url, version, tmp_file, json, error) + subroutine get_pkg_data(url, version, tmp_pkg_file, json, error) character(*), intent(in) :: url type(version_t), allocatable, intent(in) :: version - character(*), intent(in) :: tmp_file + character(*), intent(in) :: tmp_pkg_file type(json_object), intent(out) :: json type(error_t), allocatable, intent(out) :: error @@ -1372,20 +1372,20 @@ subroutine get_pkg_data(url, version, tmp_file, json, error) json = cast_to_object(j_value) end - subroutine get_file(url, tmp_file, error) + subroutine get_file(url, tmp_pkg_file, error) character(*), intent(in) :: url - character(*), intent(in) :: tmp_file + character(*), intent(in) :: tmp_pkg_file type(error_t), allocatable, intent(out) :: error end - subroutine unpack_mock_package(tmp_file, destination, error) - character(*), intent(in) :: tmp_file + subroutine unpack_mock_package(tmp_pkg_file, destination, error) + character(*), intent(in) :: tmp_pkg_file character(*), intent(in) :: destination type(error_t), allocatable, intent(out) :: error integer :: stat - call execute_command_line('cp '//tmp_file//' '//destination, exitstat=stat) + call execute_command_line('cp '//tmp_pkg_file//' '//destination, exitstat=stat) if (stat /= 0) then call test_failed(error, "Failed to create mock package"); return From 0c590794729fc8a859c9dfca2a9f5a886897ec75 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 20:40:31 +0700 Subject: [PATCH 186/799] Only read global settings once --- src/fpm/dependency.f90 | 13 ++++++++----- test/fpm_test/test_package_dependencies.f90 | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 7e1a694f28..61487e8bfd 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -524,10 +524,14 @@ subroutine resolve_dependencies(self, root, error) !> Error handling type(error_t), allocatable, intent(out) :: error + type(fpm_global_settings) :: global_settings integer :: ii + call get_global_settings(global_settings, error) + if (allocated(error)) return + do ii = 1, self%ndep - call self%resolve(self%dep(ii), root, error) + call self%resolve(self%dep(ii), global_settings, root, error) if (allocated(error)) exit end do @@ -536,11 +540,13 @@ subroutine resolve_dependencies(self, root, error) end subroutine resolve_dependencies !> Resolve a single dependency node - subroutine resolve_dependency(self, dependency, root, error) + subroutine resolve_dependency(self, dependency, global_settings, root, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> Dependency configuration to add type(dependency_node_t), intent(inout) :: dependency + !> Global configuration settings. + type(fpm_global_settings), intent(in) :: global_settings !> Current installation prefix character(len=*), intent(in) :: root !> Error handling @@ -548,7 +554,6 @@ subroutine resolve_dependency(self, dependency, root, error) type(package_config_t) :: package character(len=:), allocatable :: manifest, proj_dir, revision - type(fpm_global_settings) :: global_settings logical :: fetch if (dependency%done) return @@ -565,8 +570,6 @@ subroutine resolve_dependency(self, dependency, root, error) if (allocated(error)) return end if else - call get_global_settings(global_settings, error) - if (allocated(error)) return call dependency%get_from_registry(proj_dir, global_settings, error) if (allocated(error)) return end if diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 4b2f5bcfc4..e7600bc6b4 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -1314,11 +1314,13 @@ subroutine pkg_data_invalid_version(error) end subroutine pkg_data_invalid_version !> Resolve a single dependency node - subroutine resolve_dependency_once(self, dependency, root, error) + subroutine resolve_dependency_once(self, dependency, global_settings, root, error) !> Mock instance of the dependency tree class(mock_dependency_tree_t), intent(inout) :: self !> Dependency configuration to add type(dependency_node_t), intent(inout) :: dependency + !> Global configuration settings. + type(fpm_global_settings), intent(in) :: global_settings !> Current installation prefix character(len=*), intent(in) :: root !> Error handling From 09d09e10171b52b4e843da68e3aa56c61cce3eaf Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 12:52:17 +0200 Subject: [PATCH 187/799] Version file generation script --- ci/version.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100755 ci/version.sh diff --git a/ci/version.sh b/ci/version.sh new file mode 100755 index 0000000000..72733f1580 --- /dev/null +++ b/ci/version.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -ex + +# Helper function that wraps a string into a fortran character(*), parameter definition +fortran_character_parameter() +{ + line="character(len=*), parameter :: $1 = \"$2\"" + echo $line +} + +# define include file for version caching +INCLUDE_FILE="$(dirname $0)/../include/fpm_version_parameters.f90" + +# Get latest release version. Exclude trunk, which is named `current` on the fpm repo +latest_release=$(git describe --tags --exclude current) +if [ $? -ne 0 ]; then + echo "Could not query the current release from git. Check that git is installed on this system." + exit 1 +fi + +# Extract numbered version +no_v=${latest_release#*v} # Remove heading v +no_commit=${no_v%-*} # Remove commit # +version=${no_commit%-*} # Remove increment + +echo $no_v +echo $no_commit +echo $version + +# Write to a fortran include file +echo $(fortran_character_parameter fpm_version_ID $version ) > $INCLUDE_FILE +echo $(fortran_character_parameter fpm_version_long $latest_release ) >> $INCLUDE_FILE + From c9a35da2f3c720d3ff744820998d9d640200ac90 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 12:52:33 +0200 Subject: [PATCH 188/799] add include/ folder to fpm manifest --- fpm.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fpm.toml b/fpm.toml index ec70e34043..a5b43826a5 100644 --- a/fpm.toml +++ b/fpm.toml @@ -11,6 +11,9 @@ toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" +[library] +include-dir = "include" + [[test]] name = "cli-test" source-dir = "test/cli_test" From f37e9f1e171417011bd4c73477ce240db16a44f5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 12:53:01 +0200 Subject: [PATCH 189/799] add dummy version file --- include/fpm_version_parameters.f90 | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 include/fpm_version_parameters.f90 diff --git a/include/fpm_version_parameters.f90 b/include/fpm_version_parameters.f90 new file mode 100644 index 0000000000..07cec2c6ce --- /dev/null +++ b/include/fpm_version_parameters.f90 @@ -0,0 +1,2 @@ +character(len=*), parameter :: fpm_version_ID = "0.7.0" +character(len=*), parameter :: fpm_version_long = "v0.7.0-60-g55d94b0e" From 44b0a2ad2fbb8ad531dc320c70a6a1899c7cb310 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 12:54:37 +0200 Subject: [PATCH 190/799] create a release_parameters module --- src/fpm/fpm_release.f90 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/fpm/fpm_release.f90 diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 new file mode 100644 index 0000000000..42f9af1269 --- /dev/null +++ b/src/fpm/fpm_release.f90 @@ -0,0 +1,25 @@ +!># Release parameters +!> Module fpm_release contains public constants storing this build's unique version IDs +module fpm_release_parameters + use fpm_versioning, only: version_t,new_version + use fpm_error, only: error_t, fpm_stop + implicit none + + public :: fpm_version + + include "fpm_version_parameters.f90" + + contains + + !> Return the current fpm version from fpm_version_ID as a version type + type(version_t) function fpm_version() + + type(error_t), allocatable :: error + + call new_version(fpm_version,fpm_version_ID,error) + + if (allocated(error)) call fpm_stop(1,'*fpm*:internal error: cannot get version - '//error%message) + + end function fpm_version + +end module fpm_release_parameters From 642fbb5545955349a42d31e921a66923c422efbd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 12:54:53 +0200 Subject: [PATCH 191/799] test fpm can query its version --- test/fpm_test/test_versioning.f90 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/fpm_test/test_versioning.f90 b/test/fpm_test/test_versioning.f90 index b309d1382c..0ee9ad996f 100644 --- a/test/fpm_test/test_versioning.f90 +++ b/test/fpm_test/test_versioning.f90 @@ -2,6 +2,7 @@ module test_versioning use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_versioning + use fpm_release_parameters, only: fpm_version implicit none private @@ -18,6 +19,7 @@ subroutine collect_versioning(testsuite) type(unittest_t), allocatable, intent(out) :: testsuite(:) testsuite = [ & + & new_unittest("fpm-version", test_fpm_version), & & new_unittest("valid-version", test_valid_version), & & new_unittest("valid-equals", test_valid_equals), & & new_unittest("valid-notequals", test_valid_notequals), & @@ -32,6 +34,17 @@ subroutine collect_versioning(testsuite) end subroutine collect_versioning + !> Test fpm self-version query + subroutine test_fpm_version(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(version_t) :: self_version + + self_version = fpm_version() + + end subroutine test_fpm_version !> Read valid version strings subroutine test_valid_version(error) From db0e4709384b6cafb1324948683178558f71a4ae Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 15:35:10 +0200 Subject: [PATCH 192/799] create metapackages config --- example_packages/metapackage_openmp/fpm.toml | 4 + example_packages/metapackage_stdlib/fpm.toml | 4 + src/fpm/manifest/build.f90 | 27 +----- src/fpm/manifest/meta.f90 | 99 ++++++++++++++++++++ src/fpm/manifest/package.f90 | 18 +++- src/fpm_meta.f90 | 6 +- 6 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 src/fpm/manifest/meta.f90 diff --git a/example_packages/metapackage_openmp/fpm.toml b/example_packages/metapackage_openmp/fpm.toml index 92105e5d8c..051a88f2d2 100644 --- a/example_packages/metapackage_openmp/fpm.toml +++ b/example_packages/metapackage_openmp/fpm.toml @@ -4,10 +4,14 @@ license = "license" author = "Federico Perini" maintainer = "federico.perini@hello.world" copyright = "Copyright 2023, Federico Perini and the fpm maintainers" + [build] auto-executables = true auto-tests = true auto-examples = true + +[metapackages] openmp = true + [install] library = false diff --git a/example_packages/metapackage_stdlib/fpm.toml b/example_packages/metapackage_stdlib/fpm.toml index 50faf70022..8e11f13458 100644 --- a/example_packages/metapackage_stdlib/fpm.toml +++ b/example_packages/metapackage_stdlib/fpm.toml @@ -4,10 +4,14 @@ license = "license" author = "Federico Perini" maintainer = "federico.perini@hello.world" copyright = "Copyright 2023, Federico Perini and the fpm maintainers" + [build] auto-executables = true auto-tests = true auto-examples = true + +[metapackages] stdlib = true + [install] library = false diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 3d505b93a2..fb7fae4c42 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -34,15 +34,7 @@ module fpm_manifest_build logical :: module_naming = .false. type(string_t) :: module_prefix - !> Metapackages - !> @note when several metapackages are supported, this will need be generalized - - !> Request OpenMP support - logical :: openmp = .false. - - !> Request stdlib support - logical :: stdlib = .false. - + !> Libraries to link against !> Libraries to link against type(string_t), allocatable :: link(:) @@ -128,19 +120,6 @@ subroutine new_build_config(self, table, error) end if - !> Metapackages: read all flags - call get_value(table, "openmp", self%openmp, .false., stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'openmp' in fpm.toml, expecting logical") - return - end if - - call get_value(table, "stdlib", self%stdlib, .false., stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'stdlib' in fpm.toml, expecting logical") - return - end if - call get_list(table, "link", self%link, error) if (allocated(error)) return @@ -175,10 +154,6 @@ subroutine check(table, error) case ("module-naming") continue - !> Supported metapackages - case ("openmp","stdlib") - continue - case default call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [build]") exit diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 new file mode 100644 index 0000000000..1d7d8c5ac4 --- /dev/null +++ b/src/fpm/manifest/meta.f90 @@ -0,0 +1,99 @@ +!> Implementation of the metapackage configuration data. +!> +!> A metapackage table can currently have the following fields +!> +!>```toml +!>[metapackages] +!>fpm = "0.1.0" +!>openmp = bool +!>stdlib = bool +!>``` +module fpm_manifest_metapackages + use fpm_error, only: error_t, fatal_error, syntax_error + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + implicit none + private + + public :: metapackage_config_t, new_meta_config + + !> Configuration data for metapackages + type :: metapackage_config_t + + !> Request OpenMP support + logical :: openmp = .false. + + !> Request stdlib support + logical :: stdlib = .false. + + + end type metapackage_config_t + + +contains + + + !> Construct a new build configuration from a TOML data structure + subroutine new_meta_config(self, table, error) + + !> Instance of the build configuration + type(metapackage_config_t), intent(out) :: self + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + call check(table, error) + if (allocated(error)) return + + call get_value(table, "openmp", self%openmp, .false., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'openmp' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "stdlib", self%stdlib, .false., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'stdlib' in fpm.toml, expecting logical") + return + end if + + end subroutine new_meta_config + + !> Check local schema for allowed entries + subroutine check(table, error) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_key), allocatable :: list(:) + integer :: ikey + + call table%get_keys(list) + + ! table can be empty + if (size(list) < 1) return + + do ikey = 1, size(list) + select case(list(ikey)%key) + + !> Supported metapackages + case ("openmp","stdlib") + continue + + case default + call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [metapackages]") + exit + + end select + end do + + end subroutine check + +end module fpm_manifest_metapackages diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index de124a0b3e..32c6fb3fda 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -42,6 +42,7 @@ module fpm_manifest_package use fpm_manifest_install, only: install_config_t, new_install_config use fpm_manifest_test, only : test_config_t, new_test use fpm_mainfest_preprocess, only : preprocess_config_t, new_preprocessors + use fpm_manifest_metapackages, only: metapackage_config_t, new_meta_config use fpm_filesystem, only : exists, getline, join_path use fpm_error, only : error_t, fatal_error, syntax_error, bad_name_error use fpm_toml, only : toml_table, toml_array, toml_key, toml_stat, get_value, & @@ -72,6 +73,9 @@ module fpm_manifest_package !> Build configuration data type(build_config_t) :: build + !> Metapackage data + type(metapackage_config_t) :: meta + !> Installation configuration data type(install_config_t) :: install @@ -165,6 +169,14 @@ subroutine new_package(self, table, root, error) call new_build_config(self%build, child, error) if (allocated(error)) return + call get_value(table, "metapackages", child, requested=.true., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Type mismatch for metapackages entry, must be a table") + return + end if + call new_meta_config(self%meta, child, error) + if (allocated(error)) return + call get_value(table, "install", child, requested=.true., stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Type mismatch for install entry, must be a table") @@ -214,7 +226,7 @@ subroutine new_package(self, table, root, error) call new_library(self%library, child, error) if (allocated(error)) return end if - + call get_value(table, "profiles", child, requested=.false.) if (associated(child)) then call new_profiles(self%profiles, child, error) @@ -328,7 +340,7 @@ subroutine check(table, error) case("version", "license", "author", "maintainer", "copyright", & & "description", "keywords", "categories", "homepage", "build", & & "dependencies", "dev-dependencies", "profiles", "test", "executable", & - & "example", "library", "install", "extra", "preprocess") + & "example", "library", "install", "extra", "preprocess", "metapackages") continue end select @@ -424,7 +436,7 @@ subroutine info(self, unit, verbosity) call self%dev_dependency(ii)%info(unit, pr - 1) end do end if - + if (allocated(self%profiles)) then if (size(self%profiles) > 1 .or. pr > 2) then write(unit, fmti) "- profiles", size(self%profiles) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index a817a36cb4..879d6ac98a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -287,7 +287,7 @@ subroutine resolve_metapackage_model(model,package,error) end if ! OpenMP - if (package%build%openmp) then + if (package%meta%openmp) then call add_metapackage_model(model,"openmp",error) if (allocated(error)) return call add_metapackage_config(package,model%compiler,"openmp",error) @@ -295,7 +295,7 @@ subroutine resolve_metapackage_model(model,package,error) endif ! stdlib - if (package%build%stdlib) then + if (package%meta%stdlib) then call add_metapackage_model(model,"stdlib",error) if (allocated(error)) return call add_metapackage_config(package,model%compiler,"stdlib",error) @@ -303,7 +303,7 @@ subroutine resolve_metapackage_model(model,package,error) endif ! Stdlib is not 100% thread safe. print a warning to the user - if (package%build%stdlib .and. package%build%openmp) then + if (package%meta%stdlib .and. package%meta%openmp) then write(stdout,'(a)')' both openmp and stdlib requested: some functions may not be thread-safe!' end if From c09f16b6ce3e5a067beab86e3a507eaee1e1ef75 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 15:50:50 +0200 Subject: [PATCH 193/799] introduce MPI keyword --- src/fpm/manifest/meta.f90 | 11 ++++++++++- src/fpm_meta.f90 | 10 +++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index 1d7d8c5ac4..e3f21fd6ea 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -19,6 +19,9 @@ module fpm_manifest_metapackages !> Configuration data for metapackages type :: metapackage_config_t + !> Request MPI support + logical :: mpi = .false. + !> Request OpenMP support logical :: openmp = .false. @@ -61,6 +64,12 @@ subroutine new_meta_config(self, table, error) return end if + call get_value(table, "mpi", self%mpi, .false., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'mpi' in fpm.toml, expecting logical") + return + end if + end subroutine new_meta_config !> Check local schema for allowed entries @@ -84,7 +93,7 @@ subroutine check(table, error) select case(list(ikey)%key) !> Supported metapackages - case ("openmp","stdlib") + case ("openmp","stdlib","mpi") continue case default diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 879d6ac98a..f97484a2d7 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -98,7 +98,7 @@ subroutine init_from_name(this,name,compiler,error) case("openmp"); call init_openmp(this,compiler,error) case("stdlib"); call init_stdlib(this,compiler,error) case default - call syntax_error(error, "Metapackage "//name//" is not supported in [build]") + call syntax_error(error, "Package "//name//" is not supported in [metapackages]") return end select @@ -307,6 +307,14 @@ subroutine resolve_metapackage_model(model,package,error) write(stdout,'(a)')' both openmp and stdlib requested: some functions may not be thread-safe!' end if + ! MPI + if (package%meta%mpi) then + call add_metapackage_model(model,"mpi",error) + if (allocated(error)) return + call add_metapackage_config(package,model%compiler,"mpi",error) + if (allocated(error)) return + endif + end subroutine resolve_metapackage_model end module fpm_meta From ce02cbea2dc84474ec64d6218d1fa383c5f126ab Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 15:52:38 +0200 Subject: [PATCH 194/799] write MPI Fortran test program --- example_packages/metapackage_mpi/README.md | 7 +++++ example_packages/metapackage_mpi/app/main.f90 | 30 +++++++++++++++++++ example_packages/metapackage_mpi/fpm.toml | 15 ++++++++++ 3 files changed, 52 insertions(+) create mode 100644 example_packages/metapackage_mpi/README.md create mode 100644 example_packages/metapackage_mpi/app/main.f90 create mode 100644 example_packages/metapackage_mpi/fpm.toml diff --git a/example_packages/metapackage_mpi/README.md b/example_packages/metapackage_mpi/README.md new file mode 100644 index 0000000000..e1ea0c2194 --- /dev/null +++ b/example_packages/metapackage_mpi/README.md @@ -0,0 +1,7 @@ +# test_mpi +This test program prints the running thread ID using MPI. +PLEASE NOTE: +- Test app uses 'mpif.h' and not 'use mpi' or 'use mpi_f08' because the latter are compiler-dependent, + and the MPI implementation on the local machine may not offer an implementation for them with the same + compiler that fpm is using. +- Using mpif.h will be the most backward compatible and platform agnostic diff --git a/example_packages/metapackage_mpi/app/main.f90 b/example_packages/metapackage_mpi/app/main.f90 new file mode 100644 index 0000000000..f3c3bde606 --- /dev/null +++ b/example_packages/metapackage_mpi/app/main.f90 @@ -0,0 +1,30 @@ +program with_mpi + + include 'mpif.h' + + integer, parameter :: INIT_ERROR = 1 + integer, parameter :: RANK_ERROR = 2 + + integer :: ierror,ncpus,cpuid + + ! Initialize MPI argument + call MPI_INIT(ierror); + if (ierror/=0) stop INIT_ERROR + + ! Get number of processes and current rank + call MPI_Comm_size(MPI_COMM_WORLD, ncpus, ierror) + if (ierror/=0) stop RANK_ERROR + + call MPI_Comm_rank(MPI_COMM_WORLD, cpuid, ierror) + if (ierror/=0) stop RANK_ERROR + + print "('Hello, mpi world from rank ',i0,' of ',i0,'!')", cpuid+1,ncpu + + ! Finalize MPI environment. + call MPI_FINALIZE(ierror) + if (ierror/=0) stop INIT_ERROR + + stop 0 + +end program with_mpi + diff --git a/example_packages/metapackage_mpi/fpm.toml b/example_packages/metapackage_mpi/fpm.toml new file mode 100644 index 0000000000..398aa0ee35 --- /dev/null +++ b/example_packages/metapackage_mpi/fpm.toml @@ -0,0 +1,15 @@ +name = "test_mpi" +version = "0.1.0" +license = "license" +author = "Federico Perini" +maintainer = "federico.perini@hello.world" +copyright = "Copyright 2023, Federico Perini and the fpm maintainers" + +[build] +auto-executables = true + +[metapackages] +mpi = true + +[install] +library = false From de84fe880f2d2cd6851379211be70d5ed4d14162 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 15:57:15 +0200 Subject: [PATCH 195/799] C MPI test program --- example_packages/metapackage_mpi_c/README.md | 7 ++++++ example_packages/metapackage_mpi_c/app/main.c | 25 +++++++++++++++++++ example_packages/metapackage_mpi_c/fpm.toml | 16 ++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 example_packages/metapackage_mpi_c/README.md create mode 100644 example_packages/metapackage_mpi_c/app/main.c create mode 100644 example_packages/metapackage_mpi_c/fpm.toml diff --git a/example_packages/metapackage_mpi_c/README.md b/example_packages/metapackage_mpi_c/README.md new file mode 100644 index 0000000000..e1ea0c2194 --- /dev/null +++ b/example_packages/metapackage_mpi_c/README.md @@ -0,0 +1,7 @@ +# test_mpi +This test program prints the running thread ID using MPI. +PLEASE NOTE: +- Test app uses 'mpif.h' and not 'use mpi' or 'use mpi_f08' because the latter are compiler-dependent, + and the MPI implementation on the local machine may not offer an implementation for them with the same + compiler that fpm is using. +- Using mpif.h will be the most backward compatible and platform agnostic diff --git a/example_packages/metapackage_mpi_c/app/main.c b/example_packages/metapackage_mpi_c/app/main.c new file mode 100644 index 0000000000..2bc56c4f0c --- /dev/null +++ b/example_packages/metapackage_mpi_c/app/main.c @@ -0,0 +1,25 @@ +// Test MPI linking from a C main program +#include +#include + +int main(int argc, char** argv) +{ + + int ierror,ncpus,cpuid; + + // Initialize MPI argument + MPI_Init(&argc, &argv); + + // Get number of processes and current rank + MPI_Comm_size(MPI_COMM_WORLD, &ncpus); + + // Get Rank of the current process + MPI_Comm_rank(MPI_COMM_WORLD, &cpuid); + + printf("Hello, MPI C World from rank %d of %d! \n",cpuid+1,ncpus); + + // Finalize MPI environment. + MPI_Finalize(); + return 0; +} + diff --git a/example_packages/metapackage_mpi_c/fpm.toml b/example_packages/metapackage_mpi_c/fpm.toml new file mode 100644 index 0000000000..d5e18bac92 --- /dev/null +++ b/example_packages/metapackage_mpi_c/fpm.toml @@ -0,0 +1,16 @@ +name = "test_mpi_c" +version = "0.1.0" +license = "license" +author = "Federico Perini" +maintainer = "federico.perini@hello.world" +copyright = "Copyright 2023, Federico Perini and the fpm maintainers" + +[[executable]] +name = "test-mpi-c-main" +main = "main.c" + +[metapackages] +mpi = true + +[install] +library = false From 29be62f38cc28c55d8ff73823c93f2d9fce8e657 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 16:29:38 +0200 Subject: [PATCH 196/799] add C++ test program; cleanup --- example_packages/metapackage_mpi_c/README.md | 5 --- example_packages/metapackage_mpi_c/app/main.c | 15 ++++++-- .../metapackage_mpi_cpp/README.md | 2 ++ .../metapackage_mpi_cpp/app/main.cpp | 36 +++++++++++++++++++ example_packages/metapackage_mpi_cpp/fpm.toml | 16 +++++++++ 5 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 example_packages/metapackage_mpi_cpp/README.md create mode 100644 example_packages/metapackage_mpi_cpp/app/main.cpp create mode 100644 example_packages/metapackage_mpi_cpp/fpm.toml diff --git a/example_packages/metapackage_mpi_c/README.md b/example_packages/metapackage_mpi_c/README.md index e1ea0c2194..6ec9d07215 100644 --- a/example_packages/metapackage_mpi_c/README.md +++ b/example_packages/metapackage_mpi_c/README.md @@ -1,7 +1,2 @@ # test_mpi This test program prints the running thread ID using MPI. -PLEASE NOTE: -- Test app uses 'mpif.h' and not 'use mpi' or 'use mpi_f08' because the latter are compiler-dependent, - and the MPI implementation on the local machine may not offer an implementation for them with the same - compiler that fpm is using. -- Using mpif.h will be the most backward compatible and platform agnostic diff --git a/example_packages/metapackage_mpi_c/app/main.c b/example_packages/metapackage_mpi_c/app/main.c index 2bc56c4f0c..ab20ac1d16 100644 --- a/example_packages/metapackage_mpi_c/app/main.c +++ b/example_packages/metapackage_mpi_c/app/main.c @@ -8,7 +8,11 @@ int main(int argc, char** argv) int ierror,ncpus,cpuid; // Initialize MPI argument - MPI_Init(&argc, &argv); + ierror = MPI_Init(&argc, &argv); + if (ierror) { + printf("MPI_Init failed with error %d \n",ierror); + return 1; + } // Get number of processes and current rank MPI_Comm_size(MPI_COMM_WORLD, &ncpus); @@ -19,7 +23,12 @@ int main(int argc, char** argv) printf("Hello, MPI C World from rank %d of %d! \n",cpuid+1,ncpus); // Finalize MPI environment. - MPI_Finalize(); - return 0; + ierror = MPI_Finalize(); + if (ierror) { + printf("MPI_Finalize failed with error %d \n",ierror); + return 1; + } else { + return 0; + } } diff --git a/example_packages/metapackage_mpi_cpp/README.md b/example_packages/metapackage_mpi_cpp/README.md new file mode 100644 index 0000000000..6ec9d07215 --- /dev/null +++ b/example_packages/metapackage_mpi_cpp/README.md @@ -0,0 +1,2 @@ +# test_mpi +This test program prints the running thread ID using MPI. diff --git a/example_packages/metapackage_mpi_cpp/app/main.cpp b/example_packages/metapackage_mpi_cpp/app/main.cpp new file mode 100644 index 0000000000..8203285a9e --- /dev/null +++ b/example_packages/metapackage_mpi_cpp/app/main.cpp @@ -0,0 +1,36 @@ +// Test MPI linking from a C main program +#include +#include + +using namespace std; + +int main(int argc, char** argv) +{ + + int ierror,ncpus,cpuid; + + // Initialize MPI argument + ierror = MPI_Init(&argc, &argv); + if (ierror) { + cout << "MPI_Init failed with error " << ierror << endl; + return 1; + } + + // Get number of processes and current rank + MPI_Comm_size(MPI_COMM_WORLD, &ncpus); + + // Get Rank of the current process + MPI_Comm_rank(MPI_COMM_WORLD, &cpuid); + + cout << "Hello, MPI C++ World from rank " << cpuid << " of " << ncpus << "!" << endl; + + // Finalize MPI environment. + ierror = MPI_Finalize(); + if (ierror) { + cout << "MPI_Finalize failed with error " << ierror << endl; + return 1; + } else { + return 0; + } +} + diff --git a/example_packages/metapackage_mpi_cpp/fpm.toml b/example_packages/metapackage_mpi_cpp/fpm.toml new file mode 100644 index 0000000000..01216ea5c6 --- /dev/null +++ b/example_packages/metapackage_mpi_cpp/fpm.toml @@ -0,0 +1,16 @@ +name = "test_mpi_cpp" +version = "0.1.0" +license = "license" +author = "Federico Perini" +maintainer = "federico.perini@hello.world" +copyright = "Copyright 2023, Federico Perini and the fpm maintainers" + +[[executable]] +name = "test-mpi-c++" +main = "main.cpp" + +[metapackages] +mpi = true + +[install] +library = false From a77504b6254de0dc9ed1770fcdb6fdeb0b2ff45f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 16:30:26 +0200 Subject: [PATCH 197/799] add MPI option; inactive --- src/fpm_meta.f90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index f97484a2d7..438ea3410b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -97,6 +97,7 @@ subroutine init_from_name(this,name,compiler,error) select case(name) case("openmp"); call init_openmp(this,compiler,error) case("stdlib"); call init_stdlib(this,compiler,error) + case("mpi"); call init_mpi (this,compiler,error) case default call syntax_error(error, "Package "//name//" is not supported in [metapackages]") return @@ -317,4 +318,18 @@ subroutine resolve_metapackage_model(model,package,error) end subroutine resolve_metapackage_model +!> Initialize MPI metapackage for the current system +subroutine init_mpi(this,compiler,error) + class(metapackage_t), intent(inout) :: this + type(compiler_t), intent(in) :: compiler + type(error_t), allocatable, intent(out) :: error + + !> Cleanup + call destroy(this) + + !> Stop for now + call fatal_error(error,"MPI dependency is recognized but not implemented yet") + +end subroutine init_mpi + end module fpm_meta From 6049c6472061434fea79a9f07feeeafa0a18d7d6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 16:30:58 +0200 Subject: [PATCH 198/799] fix exe name --- example_packages/metapackage_mpi_cpp/fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_packages/metapackage_mpi_cpp/fpm.toml b/example_packages/metapackage_mpi_cpp/fpm.toml index 01216ea5c6..da21a5d8a2 100644 --- a/example_packages/metapackage_mpi_cpp/fpm.toml +++ b/example_packages/metapackage_mpi_cpp/fpm.toml @@ -6,7 +6,7 @@ maintainer = "federico.perini@hello.world" copyright = "Copyright 2023, Federico Perini and the fpm maintainers" [[executable]] -name = "test-mpi-c++" +name = "test-mpi-cpp" main = "main.cpp" [metapackages] From 39cdeeb39bce73fbda12912ad0be04af5f8c8ec8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 3 Apr 2023 18:00:50 +0200 Subject: [PATCH 199/799] attempt several MPI wrappers and filter out the invalid ones --- src/fpm/manifest/meta.f90 | 1 + src/fpm_meta.f90 | 125 +++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index e3f21fd6ea..ebd38c6559 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -11,6 +11,7 @@ module fpm_manifest_metapackages use fpm_error, only: error_t, fatal_error, syntax_error use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + use fpm_environment implicit none private diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 438ea3410b..5b0ecceab9 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -20,6 +20,8 @@ module fpm_meta use fpm_manifest_dependency, only: dependency_config_t use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t +use fpm_environment, only: get_env,os_is_unix +use fpm_filesystem, only: run use iso_fortran_env, only: stdout => output_unit implicit none @@ -324,12 +326,131 @@ subroutine init_mpi(this,compiler,error) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error + type(string_t), allocatable :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) + !> Cleanup call destroy(this) - !> Stop for now - call fatal_error(error,"MPI dependency is recognized but not implemented yet") + !> Get all candidate MPI wrappers + call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) + + print "('MPI wrapper founds: fortran=',i0,' c=',i0,' c++=',i0)", & + size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) + + if (size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)<=0) then + call fatal_error(error,"cannot find MPI wrappers for "//compiler%name()//" compiler") + return + end if + + call fatal_error(error,"MPI is being implemented, but not available yet") + end subroutine init_mpi +!> Return several mpi wrappers, and return +subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) + type(compiler_t), intent(in) :: compiler + type(string_t), allocatable, intent(out) :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) + + ! Attempt gathering MPI wrapper names from the environment variables + c_wrappers = [string_t(get_env('MPICC' ,'mpicc'))] + cpp_wrappers = [string_t(get_env('MPICXX','mpic++'))] + fort_wrappers = [string_t(get_env('MPIFC' ,'mpifc' )),& + string_t(get_env('MPIf90','mpif90')),& + string_t(get_env('MPIf77','mpif77'))] + + if (get_os_type()==OS_WINDOWS) then + c_wrappers = [c_wrappers,string_t('mpicc.bat')] + cpp_wrappers = [cpp_wrappers,string_t('mpicxx.bat')] + fort_wrappers = [fort_wrappers,string_t('mpifc.bat')] + endif + + ! Add compiler-specific wrappers + compiler_specific: select case (compiler%id) + case (id_gcc,id_f95) + + c_wrappers = [c_wrappers,string_t('mpigcc'),string_t('mpgcc')] + cpp_wrappers = [cpp_wrappers,string_t('mpig++'),string_t('mpg++')] + fort_wrappers = [fort_wrappers,string_t('mpigfortran'),string_t('mpgfortran'),& + string_t('mpig77'),string_t('mpg77')] + + case (id_intel_classic_windows,id_intel_llvm_windows,& + id_intel_classic_nix,id_intel_classic_mac,id_intel_llvm_nix,id_intel_llvm_unknown) + + c_wrappers = [c_wrappers,string_t(get_env('I_MPI_CC','mpiicc')),string_t('mpicl.bat')] + cpp_wrappers = [cpp_wrappers,string_t(get_env('I_MPI_CXX','mpiicpc')),string_t('mpicl.bat')] + fort_wrappers = [fort_wrappers,string_t(get_env('I_MPI_F90','mpiifort')),string_t('mpif77'),& + string_t('mpif90')] + + case (id_pgi,id_nvhpc) + + c_wrappers = [c_wrappers,string_t('mpipgicc'),string_t('mpgcc')] + cpp_wrappers = [cpp_wrappers,string_t('mpipgic++')] + fort_wrappers = [fort_wrappers,string_t('mpipgifort'),string_t('mpipgf90')] + + case (id_cray) + + c_wrappers = [c_wrappers,string_t('cc')] + cpp_wrappers = [cpp_wrappers,string_t('CC')] + fort_wrappers = [fort_wrappers,string_t('ftn')] + + end select compiler_specific + + call assert_mpi_wrappers(fort_wrappers) + call assert_mpi_wrappers(c_wrappers) + call assert_mpi_wrappers(cpp_wrappers) + +end subroutine mpi_wrappers + +!> Filter out invalid/unavailable mpi wrappers +subroutine assert_mpi_wrappers(wrappers,verbose) + type(string_t), allocatable, intent(inout) :: wrappers(:) + logical, optional, intent(in) :: verbose + + integer :: i + logical, allocatable :: works(:) + + allocate(works(size(wrappers))) + + do i=1,size(wrappers) + works(i) = is_mpi_wrapper(wrappers(i),verbose) + end do + + ! Filter out non-working wrappers + wrappers = pack(wrappers,works) + +end subroutine assert_mpi_wrappers + +!> Test if an MPI wrapper works +logical function is_mpi_wrapper(wrapper,verbose) + type(string_t), intent(in) :: wrapper + logical, intent(in), optional :: verbose + + logical :: echo_local + character(:), allocatable :: redirect_str + integer :: stat,cmdstat + + if(present(verbose))then + echo_local=verbose + else + echo_local=.true. + end if + + ! No redirection and non-verbose output + if (os_is_unix()) then + redirect_str = " >/dev/null 2>&1" + else + redirect_str = " >NUL 2>&1" + end if + + if(echo_local) print *, '+ ', wrapper%s + + ! Test command + call execute_command_line(wrapper%s//redirect_str, exitstat=stat,cmdstat=cmdstat) + + ! Did this command work? + is_mpi_wrapper = cmdstat==0 + +end function is_mpi_wrapper + end module fpm_meta From 0fd030d7a09bb2a8ebd0515a09f2a29b58a583ac Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Apr 2023 17:45:34 +0200 Subject: [PATCH 200/799] remove [metapackages]; move meta to [dependencies] --- src/fpm/manifest/dependency.f90 | 36 +++++++++++++++++++++++++++++---- src/fpm/manifest/meta.f90 | 35 +++++++++----------------------- src/fpm/manifest/package.f90 | 14 +++---------- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index cf3c1a31d2..0e6e3e3d93 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -29,6 +29,7 @@ module fpm_manifest_dependency use fpm_toml, only : toml_table, toml_key, toml_stat, get_value use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS + use fpm_manifest_metapackages, only: metapackage_config_t, is_meta_package, new_meta_config implicit none private @@ -192,11 +193,14 @@ end subroutine check !> Construct new dependency array from a TOML data structure - subroutine new_dependencies(deps, table, root, error) + subroutine new_dependencies(deps, table, root, meta, error) !> Instance of the dependency configuration type(dependency_config_t), allocatable, intent(out) :: deps(:) + !> (optional) metapackages + type(metapackage_config_t), optional, intent(out) :: meta + !> Instance of the TOML data structure type(toml_table), intent(inout) :: table @@ -208,20 +212,44 @@ subroutine new_dependencies(deps, table, root, error) type(toml_table), pointer :: node type(toml_key), allocatable :: list(:) - integer :: idep, stat + logical, allocatable :: non_meta(:) + integer :: idep, stat, ndep call table%get_keys(list) ! An empty table is okay if (size(list) < 1) return - allocate(deps(size(list))) + !> If requesting metapackages, do not stop on meta keywords + if (present(meta)) then + ndep = 0 + do idep = 1, size(list) + if (is_meta_package(list(idep)%key)) cycle + ndep = ndep+1 + end do + + !> Return metapackages config from this node + call new_meta_config(meta, table, error) + if (allocated(error)) return + + else + ndep = size(list) + end if + + allocate(deps(ndep)) + ndep = 0 do idep = 1, size(list) + + ! Skip meta packages + if (present(meta) .and. is_meta_package(list(idep)%key)) cycle + + ndep = ndep+1 + call get_value(table, list(idep)%key, node, stat=stat) if (stat /= toml_stat%success) then call syntax_error(error, "Dependency "//list(idep)%key//" must be a table entry") exit end if - call new_dependency(deps(idep), node, root, error) + call new_dependency(deps(ndep), node, root, error) if (allocated(error)) exit end do diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index ebd38c6559..5a72c96db8 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -15,7 +15,7 @@ module fpm_manifest_metapackages implicit none private - public :: metapackage_config_t, new_meta_config + public :: metapackage_config_t, new_meta_config, is_meta_package !> Configuration data for metapackages type :: metapackage_config_t @@ -35,7 +35,6 @@ module fpm_manifest_metapackages contains - !> Construct a new build configuration from a TOML data structure subroutine new_meta_config(self, table, error) @@ -50,8 +49,8 @@ subroutine new_meta_config(self, table, error) integer :: stat - call check(table, error) - if (allocated(error)) return + !> The toml table is not checked here because it already passed + !> the "new_dependencies" check call get_value(table, "openmp", self%openmp, .false., stat=stat) if (stat /= toml_stat%success) then @@ -74,36 +73,22 @@ subroutine new_meta_config(self, table, error) end subroutine new_meta_config !> Check local schema for allowed entries - subroutine check(table, error) + logical function is_meta_package(key) !> Instance of the TOML data structure - type(toml_table), intent(inout) :: table - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(toml_key), allocatable :: list(:) - integer :: ikey - - call table%get_keys(list) - - ! table can be empty - if (size(list) < 1) return + character(*), intent(in) :: key - do ikey = 1, size(list) - select case(list(ikey)%key) + select case (key) !> Supported metapackages case ("openmp","stdlib","mpi") - continue + is_meta_package = .true. case default - call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [metapackages]") - exit + is_meta_package = .false. - end select - end do + end select - end subroutine check + end function is_meta_package end module fpm_manifest_metapackages diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index 32c6fb3fda..f2a9100e0b 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -169,14 +169,6 @@ subroutine new_package(self, table, root, error) call new_build_config(self%build, child, error) if (allocated(error)) return - call get_value(table, "metapackages", child, requested=.true., stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error, "Type mismatch for metapackages entry, must be a table") - return - end if - call new_meta_config(self%meta, child, error) - if (allocated(error)) return - call get_value(table, "install", child, requested=.true., stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Type mismatch for install entry, must be a table") @@ -210,13 +202,13 @@ subroutine new_package(self, table, root, error) call get_value(table, "dependencies", child, requested=.false.) if (associated(child)) then - call new_dependencies(self%dependency, child, root, error) + call new_dependencies(self%dependency, child, root, self%meta, error) if (allocated(error)) return end if call get_value(table, "dev-dependencies", child, requested=.false.) if (associated(child)) then - call new_dependencies(self%dev_dependency, child, root, error) + call new_dependencies(self%dev_dependency, child, root, error=error) if (allocated(error)) return end if @@ -340,7 +332,7 @@ subroutine check(table, error) case("version", "license", "author", "maintainer", "copyright", & & "description", "keywords", "categories", "homepage", "build", & & "dependencies", "dev-dependencies", "profiles", "test", "executable", & - & "example", "library", "install", "extra", "preprocess", "metapackages") + & "example", "library", "install", "extra", "preprocess") continue end select From 66477b19f000f336b46742e93abe5230d27b8b43 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Apr 2023 17:45:42 +0200 Subject: [PATCH 201/799] update example packages --- example_packages/metapackage_mpi/fpm.toml | 2 +- example_packages/metapackage_mpi_c/fpm.toml | 2 +- example_packages/metapackage_mpi_cpp/fpm.toml | 2 +- example_packages/metapackage_openmp/fpm.toml | 2 +- example_packages/metapackage_stdlib/fpm.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example_packages/metapackage_mpi/fpm.toml b/example_packages/metapackage_mpi/fpm.toml index 398aa0ee35..9deea93520 100644 --- a/example_packages/metapackage_mpi/fpm.toml +++ b/example_packages/metapackage_mpi/fpm.toml @@ -8,7 +8,7 @@ copyright = "Copyright 2023, Federico Perini and the fpm maintainers" [build] auto-executables = true -[metapackages] +[dependencies] mpi = true [install] diff --git a/example_packages/metapackage_mpi_c/fpm.toml b/example_packages/metapackage_mpi_c/fpm.toml index d5e18bac92..8fff9db364 100644 --- a/example_packages/metapackage_mpi_c/fpm.toml +++ b/example_packages/metapackage_mpi_c/fpm.toml @@ -9,7 +9,7 @@ copyright = "Copyright 2023, Federico Perini and the fpm maintainers" name = "test-mpi-c-main" main = "main.c" -[metapackages] +[dependencies] mpi = true [install] diff --git a/example_packages/metapackage_mpi_cpp/fpm.toml b/example_packages/metapackage_mpi_cpp/fpm.toml index da21a5d8a2..7b2c39d386 100644 --- a/example_packages/metapackage_mpi_cpp/fpm.toml +++ b/example_packages/metapackage_mpi_cpp/fpm.toml @@ -9,7 +9,7 @@ copyright = "Copyright 2023, Federico Perini and the fpm maintainers" name = "test-mpi-cpp" main = "main.cpp" -[metapackages] +[dependencies] mpi = true [install] diff --git a/example_packages/metapackage_openmp/fpm.toml b/example_packages/metapackage_openmp/fpm.toml index 051a88f2d2..9638da7b42 100644 --- a/example_packages/metapackage_openmp/fpm.toml +++ b/example_packages/metapackage_openmp/fpm.toml @@ -10,7 +10,7 @@ auto-executables = true auto-tests = true auto-examples = true -[metapackages] +[dependencies] openmp = true [install] diff --git a/example_packages/metapackage_stdlib/fpm.toml b/example_packages/metapackage_stdlib/fpm.toml index 8e11f13458..66df2f11fb 100644 --- a/example_packages/metapackage_stdlib/fpm.toml +++ b/example_packages/metapackage_stdlib/fpm.toml @@ -10,7 +10,7 @@ auto-executables = true auto-tests = true auto-examples = true -[metapackages] +[dependencies] stdlib = true [install] From ccf849606b7ce57b99b29f270d1e3fec9ddc3f7f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 10:13:42 +0200 Subject: [PATCH 202/799] replace include file with an actual Fortran module --- ci/version.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ci/version.sh b/ci/version.sh index 72733f1580..bb6a407e7a 100755 --- a/ci/version.sh +++ b/ci/version.sh @@ -1,15 +1,17 @@ +#2023-04-06 This script generates + #!/usr/bin/env bash set -ex # Helper function that wraps a string into a fortran character(*), parameter definition fortran_character_parameter() { - line="character(len=*), parameter :: $1 = \"$2\"" + line=" character(len=*), parameter :: $1 = \"$2\"" echo $line } # define include file for version caching -INCLUDE_FILE="$(dirname $0)/../include/fpm_version_parameters.f90" +INCLUDE_FILE="$(dirname $0)/../src/fpm/fpm_release_parameters.f90" # Get latest release version. Exclude trunk, which is named `current` on the fpm repo latest_release=$(git describe --tags --exclude current) @@ -28,6 +30,11 @@ echo $no_commit echo $version # Write to a fortran include file -echo $(fortran_character_parameter fpm_version_ID $version ) > $INCLUDE_FILE +MODULE_NAME=fpm_release_parameters +echo "!># $MODULE_NAME: This module was automatically generated by the fpm CI " > $INCLUDE_FILE +echo "module $MODULE_NAME" >> $INCLUDE_FILE +echo " implicit none " >> $INCLUDE_FILE +echo " public " >> $INCLUDE_FILE +echo $(fortran_character_parameter fpm_version_ID $version ) >> $INCLUDE_FILE echo $(fortran_character_parameter fpm_version_long $latest_release ) >> $INCLUDE_FILE - +echo "end module $MODULE_NAME" >> $INCLUDE_FILE From a0909c16455de55f8e22cbd8af30ce04001c2e1a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 10:14:34 +0200 Subject: [PATCH 203/799] fpm: replace included file with the module reference --- include/fpm_version_parameters.f90 | 2 -- src/fpm/fpm_release.f90 | 8 ++++---- src/fpm/fpm_release_parameters.f90 | 7 +++++++ test/fpm_test/test_versioning.f90 | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 include/fpm_version_parameters.f90 create mode 100644 src/fpm/fpm_release_parameters.f90 diff --git a/include/fpm_version_parameters.f90 b/include/fpm_version_parameters.f90 deleted file mode 100644 index 07cec2c6ce..0000000000 --- a/include/fpm_version_parameters.f90 +++ /dev/null @@ -1,2 +0,0 @@ -character(len=*), parameter :: fpm_version_ID = "0.7.0" -character(len=*), parameter :: fpm_version_long = "v0.7.0-60-g55d94b0e" diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 42f9af1269..2632955665 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -1,14 +1,14 @@ !># Release parameters !> Module fpm_release contains public constants storing this build's unique version IDs -module fpm_release_parameters +module fpm_release use fpm_versioning, only: version_t,new_version use fpm_error, only: error_t, fpm_stop + use fpm_release_parameters implicit none + private public :: fpm_version - include "fpm_version_parameters.f90" - contains !> Return the current fpm version from fpm_version_ID as a version type @@ -22,4 +22,4 @@ type(version_t) function fpm_version() end function fpm_version -end module fpm_release_parameters +end module fpm_release diff --git a/src/fpm/fpm_release_parameters.f90 b/src/fpm/fpm_release_parameters.f90 new file mode 100644 index 0000000000..f6f2068d6f --- /dev/null +++ b/src/fpm/fpm_release_parameters.f90 @@ -0,0 +1,7 @@ +!># fpm_release_parameters: This module was automatically generated by the fpm CI +module fpm_release_parameters + implicit none + public +character(len=*), parameter :: fpm_version_ID = "0.7.0" +character(len=*), parameter :: fpm_version_long = "v0.7.0-66-gccf84960" +end module fpm_release_parameters diff --git a/test/fpm_test/test_versioning.f90 b/test/fpm_test/test_versioning.f90 index 0ee9ad996f..82d319fbca 100644 --- a/test/fpm_test/test_versioning.f90 +++ b/test/fpm_test/test_versioning.f90 @@ -2,7 +2,7 @@ module test_versioning use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_versioning - use fpm_release_parameters, only: fpm_version + use fpm_release, only: fpm_version implicit none private From 6d742677b4af2d572795c11faa287dd77dbc04f2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 11:09:07 +0200 Subject: [PATCH 204/799] generalize MPI wrapper test --- src/fpm_meta.f90 | 132 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 9 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 5b0ecceab9..0cb9970e81 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -13,15 +13,15 @@ !> !> module fpm_meta -use fpm_strings, only: string_t -use fpm_error, only: error_t, fatal_error, syntax_error +use fpm_strings, only: string_t, len_trim +use fpm_error, only: error_t, fatal_error, syntax_error, fpm_stop use fpm_compiler use fpm_model use fpm_manifest_dependency, only: dependency_config_t use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix -use fpm_filesystem, only: run +use fpm_filesystem, only: run, get_temp_filename, getline use iso_fortran_env, only: stdout => output_unit implicit none @@ -334,9 +334,11 @@ subroutine init_mpi(this,compiler,error) !> Get all candidate MPI wrappers call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) - print "('MPI wrapper founds: fortran=',i0,' c=',i0,' c++=',i0)", & + print "('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0)", & size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) + !> Match available wrappers with the current compiler + if (size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)<=0) then call fatal_error(error,"cannot find MPI wrappers for "//compiler%name()//" compiler") return @@ -347,6 +349,16 @@ subroutine init_mpi(this,compiler,error) end subroutine init_mpi +!> Match +logical function mpi_compiler_match(wrapper,compiler) + type(string_t), intent(in) :: wrapper + type(compiler_t), intent(in) :: compiler + + + + +end function mpi_compiler_match + !> Return several mpi wrappers, and return subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) type(compiler_t), intent(in) :: compiler @@ -421,11 +433,103 @@ subroutine assert_mpi_wrappers(wrappers,verbose) end subroutine assert_mpi_wrappers +!> Simple call to execute_command_line involving one mpi* wrapper +subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_output) + type(string_t), intent(in) :: wrapper + type(string_t), intent(in), optional :: args(:) + logical, intent(in), optional :: verbose + integer, intent(out), optional :: exitcode + logical, intent(out), optional :: cmd_success + type(string_t), intent(out), optional :: screen_output + + logical :: echo_local + character(:), allocatable :: redirect_str,command,redirect,line + integer :: iunit,iarg,stat,cmdstat + + + if(present(verbose))then + echo_local=verbose + else + echo_local=.true. + end if + + ! No redirection and non-verbose output + if (present(screen_output)) then + redirect = get_temp_filename() + redirect_str = ">"//redirect//" 2>&1" + else + if (os_is_unix()) then + redirect_str = " >/dev/null 2>&1" + else + redirect_str = " >NUL 2>&1" + end if + end if + + ! Init command + command = wrapper%s + + add_arguments: if (present(args)) then + do iarg=1,size(args) + if (len_trim(args(iarg))<=0) cycle + command = trim(command)//' '//args(iarg)%s + end do + endif add_arguments + + + if (echo_local) print *, '+ ', command + + ! Test command + call execute_command_line(command//redirect_str,exitstat=stat,cmdstat=cmdstat) + + ! Command successful? + if (present(cmd_success)) cmd_success = cmdstat==0 + + ! Program exit code? + if (present(exitcode)) exitcode = stat + + ! Want screen output? + if (present(screen_output) .and. cmdstat==0) then + + allocate(character(len=0) :: screen_output%s) + + open(newunit=iunit,file=redirect,status='old',iostat=stat) + if (stat == 0)then + do + call getline(iunit, line, stat) + if (stat /= 0) exit + + screen_output%s = screen_output%s//new_line('a')//line + + write(*,'(A)') trim(line) + end do + + ! Close and delete file + close(iunit,status='delete') + + else + call fpm_stop(1,'cannot read temporary file from successful MPI wrapper') + endif + + end if + +end subroutine run_mpi_wrapper + !> Test if an MPI wrapper works logical function is_mpi_wrapper(wrapper,verbose) type(string_t), intent(in) :: wrapper logical, intent(in), optional :: verbose + call run_mpi_wrapper(wrapper,verbose=verbose,cmd_success=is_mpi_wrapper) + +end function is_mpi_wrapper + +!> Test if an MPI wrapper works +type(string_t) function mpi_wrapper_command(wrapper,command,verbose,error) + type(string_t), intent(in) :: wrapper + character(*), intent(in) :: command + logical, intent(in), optional :: verbose + type(error_t), allocatable, intent(out) :: error + logical :: echo_local character(:), allocatable :: redirect_str integer :: stat,cmdstat @@ -445,12 +549,22 @@ logical function is_mpi_wrapper(wrapper,verbose) if(echo_local) print *, '+ ', wrapper%s - ! Test command - call execute_command_line(wrapper%s//redirect_str, exitstat=stat,cmdstat=cmdstat) + select case (command) + case ('compiler') - ! Did this command work? - is_mpi_wrapper = cmdstat==0 + ! Return compiler name for the current MPI wrapper + call execute_command_line(wrapper%s//redirect_str, exitstat=stat,cmdstat=cmdstat) -end function is_mpi_wrapper + + + + case default; + call fatal_error(error,'an invalid MPI wrapper command ('//command//& + ') was invoked for wrapper <'//wrapper%s//'>.') + return + end select + + +end function mpi_wrapper_command end module fpm_meta From b099461c95c2e8fdc67b609f9b7433c0c9f5aec6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 11:32:36 +0200 Subject: [PATCH 205/799] identify OpenMPI wrappers --- src/fpm_meta.f90 | 108 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 29 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 0cb9970e81..fc652a1bcc 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -68,6 +68,12 @@ module fpm_meta module procedure resolve_metapackage_model end interface resolve_metapackages +integer, parameter :: MPI_TYPE_NONE = 0 +integer, parameter :: MPI_TYPE_OPENMPI = 1 +integer, parameter :: MPI_TYPE_MPICH = 2 +integer, parameter :: MPI_TYPE_INTEL = 3 +integer, parameter :: MPI_TYPE_MSMPI = 4 + contains !> Clean the metapackage structure @@ -327,6 +333,7 @@ subroutine init_mpi(this,compiler,error) type(error_t), allocatable, intent(out) :: error type(string_t), allocatable :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) + integer :: ifort,ic,icpp !> Cleanup call destroy(this) @@ -337,22 +344,38 @@ subroutine init_mpi(this,compiler,error) print "('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0)", & size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) - !> Match available wrappers with the current compiler - if (size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)<=0) then call fatal_error(error,"cannot find MPI wrappers for "//compiler%name()//" compiler") return end if + ifort = mpi_compiler_match(fort_wrappers,compiler,error) + ic = mpi_compiler_match(c_wrappers,compiler,error) + icpp = mpi_compiler_match(cpp_wrappers,compiler,error) + call fatal_error(error,"MPI is being implemented, but not available yet") end subroutine init_mpi -!> Match -logical function mpi_compiler_match(wrapper,compiler) - type(string_t), intent(in) :: wrapper +!> Match one of the available compiler wrappers with the current compiler +integer function mpi_compiler_match(wrappers,compiler,error) + type(string_t), intent(in) :: wrappers(:) type(compiler_t), intent(in) :: compiler + type(error_t), allocatable, intent(out) :: error + + integer :: i + type(string_t) :: screen + + do i=1,size(wrappers) + + screen = mpi_wrapper_query(wrappers(i),'compiler',.false.,error) + if (allocated(error)) return + + end do + + + @@ -420,16 +443,16 @@ subroutine assert_mpi_wrappers(wrappers,verbose) logical, optional, intent(in) :: verbose integer :: i - logical, allocatable :: works(:) + integer, allocatable :: works(:) allocate(works(size(wrappers))) do i=1,size(wrappers) - works(i) = is_mpi_wrapper(wrappers(i),verbose) + works(i) = which_mpi_wrapper(wrappers(i),verbose) end do ! Filter out non-working wrappers - wrappers = pack(wrappers,works) + wrappers = pack(wrappers,works/=MPI_TYPE_NONE) end subroutine assert_mpi_wrappers @@ -515,47 +538,74 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp end subroutine run_mpi_wrapper !> Test if an MPI wrapper works -logical function is_mpi_wrapper(wrapper,verbose) +integer function which_mpi_wrapper(wrapper,verbose) type(string_t), intent(in) :: wrapper logical, intent(in), optional :: verbose + logical :: is_mpi_wrapper + integer :: stat + + ! Run mpi wrapper first call run_mpi_wrapper(wrapper,verbose=verbose,cmd_success=is_mpi_wrapper) -end function is_mpi_wrapper + if (is_mpi_wrapper) then + + ! Attempt to decipher which library this wrapper comes from. + + ! OpenMPI responds to '--showme' calls + call run_mpi_wrapper(wrapper,[string_t('--showme')],verbose,& + exitcode=stat,cmd_success=is_mpi_wrapper) + + if (stat==0 .and. is_mpi_wrapper) then + + which_mpi_wrapper = MPI_TYPE_OPENMPI + + else + + ! This MPI wrapper is of a currently unsupported library + which_mpi_wrapper = MPI_TYPE_NONE + + end if + + else + + which_mpi_wrapper = MPI_TYPE_NONE + + end if + +end function which_mpi_wrapper !> Test if an MPI wrapper works -type(string_t) function mpi_wrapper_command(wrapper,command,verbose,error) +type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result(screen) type(string_t), intent(in) :: wrapper character(*), intent(in) :: command logical, intent(in), optional :: verbose type(error_t), allocatable, intent(out) :: error - logical :: echo_local + logical :: success character(:), allocatable :: redirect_str - integer :: stat,cmdstat + integer :: stat,cmdstat,mpi - if(present(verbose))then - echo_local=verbose - else - echo_local=.true. - end if - - ! No redirection and non-verbose output - if (os_is_unix()) then - redirect_str = " >/dev/null 2>&1" - else - redirect_str = " >NUL 2>&1" + ! Get mpi type + mpi = which_mpi_wrapper(wrapper,verbose) + if (mpi==MPI_TYPE_NONE) then + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return end if - if(echo_local) print *, '+ ', wrapper%s - select case (command) case ('compiler') - ! Return compiler name for the current MPI wrapper - call execute_command_line(wrapper%s//redirect_str, exitstat=stat,cmdstat=cmdstat) + ! Try one of the available "showme" options + call run_mpi_wrapper(wrapper,[string_t('--showme')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + if (success .and. stat==0) then + print *, 'screen output = ',screen%s + else + print *, 'mpi wrapper unsuccessful' + end if case default; @@ -565,6 +615,6 @@ type(string_t) function mpi_wrapper_command(wrapper,command,verbose,error) end select -end function mpi_wrapper_command +end function mpi_wrapper_query end module fpm_meta From e2792d90fe94d8026b7790e777db41fc5c4d1481 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 12:43:46 +0200 Subject: [PATCH 206/799] get libraries, include directories, linking directories from OpenMPI wrapper --- src/fpm_meta.f90 | 192 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 162 insertions(+), 30 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index fc652a1bcc..e3f844050b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -34,6 +34,7 @@ module fpm_meta type, public :: metapackage_t logical :: has_link_libraries = .false. + logical :: has_link_dirs = .false. logical :: has_link_flags = .false. logical :: has_build_flags = .false. logical :: has_include_dirs = .false. @@ -42,6 +43,7 @@ module fpm_meta !> List of compiler flags and options to be added type(string_t) :: flags type(string_t) :: link_flags + type(string_t), allocatable :: incl_dirs(:) type(string_t), allocatable :: link_dirs(:) type(string_t), allocatable :: link_libs(:) @@ -82,6 +84,7 @@ elemental subroutine destroy(this) this%has_link_libraries = .false. this%has_link_flags = .false. + this%has_link_dirs = .false. this%has_build_flags = .false. this%has_include_dirs = .false. this%has_dependencies = .false. @@ -91,6 +94,7 @@ elemental subroutine destroy(this) if (allocated(this%link_dirs)) deallocate(this%link_dirs) if (allocated(this%link_libs)) deallocate(this%link_libs) if (allocated(this%dependency)) deallocate(this%dependency) + if (allocated(this%incl_dirs)) deallocate(this%incl_dirs) end subroutine destroy @@ -220,7 +224,7 @@ subroutine resolve_model(self,model,error) end if if (self%has_include_dirs) then - model%include_dirs = [model%include_dirs,self%link_dirs] + model%include_dirs = [model%include_dirs,self%incl_dirs] end if ! Dependencies are resolved in the package config @@ -332,29 +336,72 @@ subroutine init_mpi(this,compiler,error) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error + logical, parameter :: verbose = .true. type(string_t), allocatable :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) - integer :: ifort,ic,icpp + type(string_t) :: output + character(256) :: msg_out + character(len=:), allocatable :: tokens(:) + integer :: ifort,ic,icpp,i !> Cleanup call destroy(this) !> Get all candidate MPI wrappers call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) - - print "('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0)", & - size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) + if (verbose) print 1, size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) if (size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)<=0) then call fatal_error(error,"cannot find MPI wrappers for "//compiler%name()//" compiler") return end if + !> Return an MPI wrapper that matches the current compiler ifort = mpi_compiler_match(fort_wrappers,compiler,error) - ic = mpi_compiler_match(c_wrappers,compiler,error) - icpp = mpi_compiler_match(cpp_wrappers,compiler,error) + if (allocated(error)) return + + !C, C++ not available yet + !ic = mpi_compiler_match(c_wrappers,compiler,error) + !icpp = mpi_compiler_match(cpp_wrappers,compiler,error) + + !> Build MPI dependency + if (ifort>0) then + + ! Get linking libraries + output = mpi_wrapper_query(fort_wrappers(ifort),'link',verbose,error) + if (allocated(error)) return + call split(output%s,tokens,delimiters=' ') + + this%has_link_libraries = size(tokens)>0 + this%link_libs = [(string_t(tokens(i)),i=1,size(tokens))] + + ! Get library directories + output = mpi_wrapper_query(fort_wrappers(ifort),'link_dirs',verbose,error) + if (allocated(error)) return + call split(output%s,tokens,delimiters=' ') + + this%has_link_dirs = size(tokens)>0 + this%link_dirs = [(string_t(tokens(i)),i=1,size(tokens))] + + ! Get include directories + output = mpi_wrapper_query(fort_wrappers(ifort),'incl_dirs',verbose,error) + if (allocated(error)) return + call split(output%s,tokens,delimiters=' ') + + this%has_include_dirs = size(tokens)>0 + this%incl_dirs = [(string_t(tokens(i)),i=1,size(tokens))] + + else - call fatal_error(error,"MPI is being implemented, but not available yet") + ! None of the available wrappers matched the current Fortran compiler + write(msg_out,1) size(fort_wrappers),compiler%fc + call fatal_error(error,trim(msg_out)) + return + endif + + + 1 format('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0) + 2 format(' None out of ',i0,' valid MPI wrappers matches compiler ',a) end subroutine init_mpi @@ -366,19 +413,33 @@ integer function mpi_compiler_match(wrappers,compiler,error) integer :: i type(string_t) :: screen + character(128) :: msg_out + type(compiler_t) :: mpi_compiler + + mpi_compiler_match = 0 do i=1,size(wrappers) screen = mpi_wrapper_query(wrappers(i),'compiler',.false.,error) if (allocated(error)) return - end do - + ! Build compiler type + call new_compiler(mpi_compiler, screen%s,'','',echo=.true.,verbose=.true.) + ! Match found! + if (mpi_compiler%id == compiler%id) then + mpi_compiler_match = i + return + end if + end do + ! None of the available wrappers matched the current Fortran compiler + write(msg_out,1) size(wrappers),compiler%fc + call fatal_error(error,trim(msg_out)) + 1 format(' None out of ',i0,' valid MPI wrappers matches compiler ',a) end function mpi_compiler_match @@ -448,7 +509,7 @@ subroutine assert_mpi_wrappers(wrappers,verbose) allocate(works(size(wrappers))) do i=1,size(wrappers) - works(i) = which_mpi_wrapper(wrappers(i),verbose) + works(i) = which_mpi_library(wrappers(i),verbose) end do ! Filter out non-working wrappers @@ -537,8 +598,8 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp end subroutine run_mpi_wrapper -!> Test if an MPI wrapper works -integer function which_mpi_wrapper(wrapper,verbose) +!> Get MPI library type from the wrapper command. Currently, only OpenMPI is supported +integer function which_mpi_library(wrapper,verbose) type(string_t), intent(in) :: wrapper logical, intent(in), optional :: verbose @@ -558,22 +619,22 @@ integer function which_mpi_wrapper(wrapper,verbose) if (stat==0 .and. is_mpi_wrapper) then - which_mpi_wrapper = MPI_TYPE_OPENMPI + which_mpi_library = MPI_TYPE_OPENMPI else ! This MPI wrapper is of a currently unsupported library - which_mpi_wrapper = MPI_TYPE_NONE + which_mpi_library = MPI_TYPE_NONE end if else - which_mpi_wrapper = MPI_TYPE_NONE + which_mpi_library = MPI_TYPE_NONE end if -end function which_mpi_wrapper +end function which_mpi_library !> Test if an MPI wrapper works type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result(screen) @@ -587,26 +648,97 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( integer :: stat,cmdstat,mpi ! Get mpi type - mpi = which_mpi_wrapper(wrapper,verbose) - if (mpi==MPI_TYPE_NONE) then - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') - return - end if + mpi = which_mpi_library(wrapper,verbose) select case (command) + + ! Get MPI compiler name case ('compiler') - ! Try one of the available "showme" options - call run_mpi_wrapper(wrapper,[string_t('--showme')],verbose=.true., & - exitcode=stat,cmd_success=success,screen_output=screen) + select case (mpi) + case (MPI_TYPE_OPENMPI) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('--showme:command')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local OpenMPI library does not support --showme:command') + return + end if + + case default + + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + + end select + + ! Get a list of MPI linked libraries + case ('link') + + select case (mpi) + case (MPI_TYPE_OPENMPI) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('--showme:libs')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local OpenMPI library does not support --showme:link') + return + end if + + case default + + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + + end select + + ! Get a list of MPI library directories + case ('link_dirs') + + select case (mpi) + case (MPI_TYPE_OPENMPI) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('--showme:libdirs')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local OpenMPI library does not support --showme:libdirs') + return + end if + + case default + + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + + end select + + ! Get a list of include directories for the MPI headers/modules + case ('incl_dirs') + + select case (mpi) + case (MPI_TYPE_OPENMPI) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('--showme:incdirs')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local OpenMPI library does not support --showme:incdirs') + return + end if - if (success .and. stat==0) then + case default - print *, 'screen output = ',screen%s - else - print *, 'mpi wrapper unsuccessful' - end if + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + end select case default; call fatal_error(error,'an invalid MPI wrapper command ('//command//& From 1a39ade71323187c6eb76301927084e47e21cc9b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 13:02:35 +0200 Subject: [PATCH 207/799] simplify to build/link flags (link dirs not supported by fpm) --- src/fpm_meta.f90 | 53 ++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e3f844050b..dabef9dcfa 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -366,29 +366,15 @@ subroutine init_mpi(this,compiler,error) !> Build MPI dependency if (ifort>0) then - ! Get linking libraries - output = mpi_wrapper_query(fort_wrappers(ifort),'link',verbose,error) + ! Get linking flags + this%link_flags = mpi_wrapper_query(fort_wrappers(ifort),'link',verbose,error) if (allocated(error)) return - call split(output%s,tokens,delimiters=' ') + this%has_link_flags = len_trim(this%link_flags)>0 - this%has_link_libraries = size(tokens)>0 - this%link_libs = [(string_t(tokens(i)),i=1,size(tokens))] - - ! Get library directories - output = mpi_wrapper_query(fort_wrappers(ifort),'link_dirs',verbose,error) - if (allocated(error)) return - call split(output%s,tokens,delimiters=' ') - - this%has_link_dirs = size(tokens)>0 - this%link_dirs = [(string_t(tokens(i)),i=1,size(tokens))] - - ! Get include directories - output = mpi_wrapper_query(fort_wrappers(ifort),'incl_dirs',verbose,error) + ! Get build flags + this%flags = mpi_wrapper_query(fort_wrappers(ifort),'flags',verbose,error) if (allocated(error)) return - call split(output%s,tokens,delimiters=' ') - - this%has_include_dirs = size(tokens)>0 - this%incl_dirs = [(string_t(tokens(i)),i=1,size(tokens))] + this%has_build_flags = len_trim(this%flags)>0 else @@ -674,14 +660,37 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( end select - ! Get a list of MPI linked libraries + + ! Get a list of additional compiler flags + case ('flags') + + select case (mpi) + case (MPI_TYPE_OPENMPI) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('--showme:compile')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local OpenMPI library does not support --showme:compile') + return + end if + + case default + + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + + end select + + ! Get a list of additional linker flags case ('link') select case (mpi) case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:libs')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:link')],verbose=.true., & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then From 9a56db65edb1af290da5a0193c6c3e864eebc274 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 14:33:28 +0200 Subject: [PATCH 208/799] remove new line characters from the wrapper flags --- src/fpm_meta.f90 | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index dabef9dcfa..1f067e13ae 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -322,6 +322,9 @@ subroutine resolve_metapackage_model(model,package,error) ! MPI if (package%meta%mpi) then + + print *, 'resolving MPI...' + call add_metapackage_model(model,"mpi",error) if (allocated(error)) return call add_metapackage_config(package,model%compiler,"mpi",error) @@ -371,11 +374,17 @@ subroutine init_mpi(this,compiler,error) if (allocated(error)) return this%has_link_flags = len_trim(this%link_flags)>0 + ! Add heading space + this%link_flags = string_t(' '//this%link_flags%s) + ! Get build flags this%flags = mpi_wrapper_query(fort_wrappers(ifort),'flags',verbose,error) if (allocated(error)) return this%has_build_flags = len_trim(this%flags)>0 + ! Add heading space + this%flags = string_t(' '//this%flags%s) + else ! None of the available wrappers matched the current Fortran compiler @@ -410,7 +419,7 @@ integer function mpi_compiler_match(wrappers,compiler,error) if (allocated(error)) return ! Build compiler type - call new_compiler(mpi_compiler, screen%s,'','',echo=.true.,verbose=.true.) + call new_compiler(mpi_compiler,screen%s,'','',echo=.true.,verbose=.true.) ! Match found! if (mpi_compiler%id == compiler%id) then @@ -676,6 +685,8 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( return end if + call remove_new_lines(screen) + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') @@ -698,6 +709,8 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( return end if + call remove_new_lines(screen) + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') @@ -758,4 +771,35 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( end function mpi_wrapper_query +! Remove all new line characters from the current string +subroutine remove_new_lines(string) + type(string_t), intent(inout) :: string + + integer :: feed,length + + if (.not.allocated(string%s)) return + + + length = len(string%s) + feed = scan(string%s,new_line('a')) + + do while (length>0 .and. feed>0) + + if (length==1) then + string = string_t("") + elseif (feed==1) then + string%s = string%s(2:length) + elseif (feed==length) then + string%s = string%s(1:length-1) + else + string%s = string%s(1:feed-1)//string%s(feed+1:length) + end if + + length = len(string%s) + feed = scan(string%s,new_line('a')) + + end do + +end subroutine remove_new_lines + end module fpm_meta From ff2744cefc363d782fee28800488c7106a3db838 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 14:34:22 +0200 Subject: [PATCH 209/799] remove link directories (not supported by fpm) --- src/fpm_meta.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 1f067e13ae..6135dfd07e 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -34,7 +34,6 @@ module fpm_meta type, public :: metapackage_t logical :: has_link_libraries = .false. - logical :: has_link_dirs = .false. logical :: has_link_flags = .false. logical :: has_build_flags = .false. logical :: has_include_dirs = .false. @@ -44,7 +43,6 @@ module fpm_meta type(string_t) :: flags type(string_t) :: link_flags type(string_t), allocatable :: incl_dirs(:) - type(string_t), allocatable :: link_dirs(:) type(string_t), allocatable :: link_libs(:) !> List of Development dependency meta data. @@ -84,14 +82,12 @@ elemental subroutine destroy(this) this%has_link_libraries = .false. this%has_link_flags = .false. - this%has_link_dirs = .false. this%has_build_flags = .false. this%has_include_dirs = .false. this%has_dependencies = .false. if (allocated(this%flags%s)) deallocate(this%flags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) - if (allocated(this%link_dirs)) deallocate(this%link_dirs) if (allocated(this%link_libs)) deallocate(this%link_libs) if (allocated(this%dependency)) deallocate(this%dependency) if (allocated(this%incl_dirs)) deallocate(this%incl_dirs) From 91d52b57d19ef42e60aa2e2721f03e6a6e4bf4b3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 15:35:52 +0200 Subject: [PATCH 210/799] clean CI script --- ci/version.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ci/version.sh b/ci/version.sh index bb6a407e7a..dce0f80bfc 100755 --- a/ci/version.sh +++ b/ci/version.sh @@ -1,12 +1,15 @@ -#2023-04-06 This script generates - #!/usr/bin/env bash + +#2023-04-06 This script generates a Fortran module file with the fpm release information, +#hardcoded as Fortran PARAMETERs. Other options using external/include files do not work +#because fpm also needs to be built as a single-source-file package. + set -ex # Helper function that wraps a string into a fortran character(*), parameter definition fortran_character_parameter() { - line=" character(len=*), parameter :: $1 = \"$2\"" + line="character(len=*), parameter :: $1 = \"$2\"" echo $line } @@ -25,9 +28,7 @@ no_v=${latest_release#*v} # Remove heading v no_commit=${no_v%-*} # Remove commit # version=${no_commit%-*} # Remove increment -echo $no_v -echo $no_commit -echo $version +echo "Deploying fpm version $version information to $INCLUDE_FILE ... # Write to a fortran include file MODULE_NAME=fpm_release_parameters From 8ae464eff9ec3136d57d5f1c0a1d0476a0d37b06 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 15:49:22 +0200 Subject: [PATCH 211/799] add fortran-regex dependency --- fpm.toml | 2 ++ src/fpm_meta.f90 | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/fpm.toml b/fpm.toml index ec70e34043..93419d38cc 100644 --- a/fpm.toml +++ b/fpm.toml @@ -10,6 +10,8 @@ toml-f.git = "https://github.com/toml-f/toml-f" toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" +fortran-regex.git = "https://github.com/perazz/fortran-regex" +fortran-regex.tag = "1.1.0" [[test]] name = "cli-test" diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 6135dfd07e..06470cf848 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -22,7 +22,9 @@ module fpm_meta use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix use fpm_filesystem, only: run, get_temp_filename, getline +use fpm_versioning, only: version_t, new_version use iso_fortran_env, only: stdout => output_unit +use regex_module, only: regex implicit none @@ -33,6 +35,9 @@ module fpm_meta !> Type for describing a source file type, public :: metapackage_t + !> Package version (if supported) + type(version_t), allocatable :: version + logical :: has_link_libraries = .false. logical :: has_link_flags = .false. logical :: has_build_flags = .false. @@ -80,12 +85,14 @@ module fpm_meta elemental subroutine destroy(this) class(metapackage_t), intent(inout) :: this + this%has_link_libraries = .false. this%has_link_flags = .false. this%has_build_flags = .false. this%has_include_dirs = .false. this%has_dependencies = .false. + if (allocated(this%version)) deallocate(this%version) if (allocated(this%flags%s)) deallocate(this%flags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) if (allocated(this%link_libs)) deallocate(this%link_libs) @@ -338,6 +345,7 @@ subroutine init_mpi(this,compiler,error) logical, parameter :: verbose = .true. type(string_t), allocatable :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) type(string_t) :: output + type(version_t) :: version character(256) :: msg_out character(len=:), allocatable :: tokens(:) integer :: ifort,ic,icpp,i @@ -381,6 +389,15 @@ subroutine init_mpi(this,compiler,error) ! Add heading space this%flags = string_t(' '//this%flags%s) + ! Get library version + version = mpi_version_get(fort_wrappers(ifort),error) + if (allocated(error)) then + return + else + allocate(this%version,source=version) + end if + + else ! None of the available wrappers matched the current Fortran compiler @@ -434,6 +451,27 @@ integer function mpi_compiler_match(wrappers,compiler,error) end function mpi_compiler_match +!> Return library version from the MPI wrapper command +type(version_t) function mpi_version_get(wrapper,error) + type(string_t), intent(in) :: wrapper + type(error_t), allocatable, intent(out) :: error + + type(string_t) :: version_line,version_string + integer :: i,length + + ! Get version string + version_line = mpi_wrapper_query(wrapper,'version',error=error) + if (allocated(error)) return + + ! Extract version + version_string = regex(version_line%s,'',length=length) + + + ! Parse version + call new_version(mpi_version_get,version_s%s,error) + +end function mpi_version_get + !> Return several mpi wrappers, and return subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) type(compiler_t), intent(in) :: compiler @@ -758,6 +796,30 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( end select + ! Retrieve library version + case ('version') + + select case (mpi) + case (MPI_TYPE_OPENMPI) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('--showme:version')],verbose=.true., & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local OpenMPI library does not support --showme:version') + return + else + call remove_new_lines(screen) + end if + + case default + + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + + end select + case default; call fatal_error(error,'an invalid MPI wrapper command ('//command//& ') was invoked for wrapper <'//wrapper%s//'>.') From e456509eba152216ed27fc64f7954e5a33f6d9b7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 6 Apr 2023 17:49:01 +0200 Subject: [PATCH 212/799] query OpenMPI version and save in metapackage_t --- src/fpm_meta.f90 | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 06470cf848..a9800d5f5e 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -397,7 +397,6 @@ subroutine init_mpi(this,compiler,error) allocate(this%version,source=version) end if - else ! None of the available wrappers matched the current Fortran compiler @@ -456,19 +455,14 @@ type(version_t) function mpi_version_get(wrapper,error) type(string_t), intent(in) :: wrapper type(error_t), allocatable, intent(out) :: error - type(string_t) :: version_line,version_string - integer :: i,length + type(string_t) :: version_line ! Get version string version_line = mpi_wrapper_query(wrapper,'version',error=error) if (allocated(error)) return - ! Extract version - version_string = regex(version_line%s,'',length=length) - - - ! Parse version - call new_version(mpi_version_get,version_s%s,error) + ! Wrap to object + call new_version(mpi_version_get,version_line%s,error) end function mpi_version_get @@ -674,7 +668,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( logical :: success character(:), allocatable :: redirect_str - integer :: stat,cmdstat,mpi + integer :: stat,cmdstat,mpi,ire,length ! Get mpi type mpi = which_mpi_library(wrapper,verbose) @@ -813,6 +807,20 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( call remove_new_lines(screen) end if + ! Extract version + ire = regex(screen%s,'\d+.\d+.\d+',length=length) + + if (ire>0 .and. length>0) then + + ! Parse version into the object (this should always work) + screen%s = screen%s(ire:ire+length-1) + + else + + call syntax_error(error,'cannot retrieve OpenMPI library version.') + + end if + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') From ed27f9bf1cb20c8fb12d2570397cf711a162c77f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 6 Apr 2023 23:15:59 +0700 Subject: [PATCH 213/799] Remove duplication --- src/fpm/dependency.f90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 61487e8bfd..626b1d37a7 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -475,9 +475,6 @@ subroutine update_dependency(self, name, error) associate (dep => self%dep(id)) if (allocated(dep%git) .and. dep%update) then - if (self%verbosity > 1) then - write (self%unit, out_fmt) "Update:", dep%name - end if write (self%unit, out_fmt) "Update:", dep%name proj_dir = join_path(self%dep_dir, dep%name) call dep%git%checkout(proj_dir, error) @@ -862,7 +859,7 @@ pure logical function has_dependency(self, dependency) !> Instance of the dependency tree class(dependency_tree_t), intent(in) :: self !> Dependency configuration to check - class(dependency_config_t), intent(in) :: dependency + class(dependency_node_t), intent(in) :: dependency has_dependency = self%find(dependency%name) /= 0 From a4f2e1a48052759956e71bf960ab65ec5227db67 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 09:19:48 +0200 Subject: [PATCH 214/799] replace bash script with manifest macro --- ci/version.sh | 41 ------------------------------ fpm.toml | 4 +++ src/fpm/fpm_release.f90 | 6 ++++- src/fpm/fpm_release_parameters.f90 | 7 ----- 4 files changed, 9 insertions(+), 49 deletions(-) delete mode 100755 ci/version.sh delete mode 100644 src/fpm/fpm_release_parameters.f90 diff --git a/ci/version.sh b/ci/version.sh deleted file mode 100755 index dce0f80bfc..0000000000 --- a/ci/version.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -#2023-04-06 This script generates a Fortran module file with the fpm release information, -#hardcoded as Fortran PARAMETERs. Other options using external/include files do not work -#because fpm also needs to be built as a single-source-file package. - -set -ex - -# Helper function that wraps a string into a fortran character(*), parameter definition -fortran_character_parameter() -{ - line="character(len=*), parameter :: $1 = \"$2\"" - echo $line -} - -# define include file for version caching -INCLUDE_FILE="$(dirname $0)/../src/fpm/fpm_release_parameters.f90" - -# Get latest release version. Exclude trunk, which is named `current` on the fpm repo -latest_release=$(git describe --tags --exclude current) -if [ $? -ne 0 ]; then - echo "Could not query the current release from git. Check that git is installed on this system." - exit 1 -fi - -# Extract numbered version -no_v=${latest_release#*v} # Remove heading v -no_commit=${no_v%-*} # Remove commit # -version=${no_commit%-*} # Remove increment - -echo "Deploying fpm version $version information to $INCLUDE_FILE ... - -# Write to a fortran include file -MODULE_NAME=fpm_release_parameters -echo "!># $MODULE_NAME: This module was automatically generated by the fpm CI " > $INCLUDE_FILE -echo "module $MODULE_NAME" >> $INCLUDE_FILE -echo " implicit none " >> $INCLUDE_FILE -echo " public " >> $INCLUDE_FILE -echo $(fortran_character_parameter fpm_version_ID $version ) >> $INCLUDE_FILE -echo $(fortran_character_parameter fpm_version_long $latest_release ) >> $INCLUDE_FILE -echo "end module $MODULE_NAME" >> $INCLUDE_FILE diff --git a/fpm.toml b/fpm.toml index a5b43826a5..392e9a0b97 100644 --- a/fpm.toml +++ b/fpm.toml @@ -11,6 +11,10 @@ toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" +[preprocess] +[preprocess.cpp] +macros=["FPM_VERSION={version}"] + [library] include-dir = "include" diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 2632955665..1c0c1f180c 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -3,10 +3,14 @@ module fpm_release use fpm_versioning, only: version_t,new_version use fpm_error, only: error_t, fpm_stop - use fpm_release_parameters implicit none private +#ifndef FPM_VERSION +#define FPM_VERSION UNDEFINED +#endif + character(len=*), parameter :: fpm_version_ID = "FPM_VERSION" + public :: fpm_version contains diff --git a/src/fpm/fpm_release_parameters.f90 b/src/fpm/fpm_release_parameters.f90 deleted file mode 100644 index f6f2068d6f..0000000000 --- a/src/fpm/fpm_release_parameters.f90 +++ /dev/null @@ -1,7 +0,0 @@ -!># fpm_release_parameters: This module was automatically generated by the fpm CI -module fpm_release_parameters - implicit none - public -character(len=*), parameter :: fpm_version_ID = "0.7.0" -character(len=*), parameter :: fpm_version_long = "v0.7.0-66-gccf84960" -end module fpm_release_parameters From 168331f2b6a0d4fab0cb1d2102e8d49228f28c77 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 09:22:52 +0200 Subject: [PATCH 215/799] keep version in `fpm --version` updated --- src/fpm/fpm_release.f90 | 1 + src/fpm_command_line.f90 | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 1c0c1f180c..09250be3b1 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -12,6 +12,7 @@ module fpm_release character(len=*), parameter :: fpm_version_ID = "FPM_VERSION" public :: fpm_version + public :: version_t contains diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 659acd1950..b939bea67c 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -33,6 +33,7 @@ module fpm_command_line use fpm_environment, only : get_command_arguments_quoted use fpm_error, only : fpm_stop, error_t use fpm_os, only : get_current_directory +use fpm_release, only : fpm_version, version_t use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, & & stdout=>output_unit, & & stderr=>error_unit @@ -209,8 +210,9 @@ subroutine get_command_line_settings(cmd_settings) integer :: os logical :: unix type(fpm_install_settings), allocatable :: install_settings + type(version_t) :: version character(len=:), allocatable :: common_args, compiler_args, run_args, working_dir, & - & c_compiler, cxx_compiler, archiver + & c_compiler, cxx_compiler, archiver, version_s character(len=*), parameter :: fc_env = "FC", cc_env = "CC", ar_env = "AR", & & fflags_env = "FFLAGS", cflags_env = "CFLAGS", cxxflags_env = "CXXFLAGS", ldflags_env = "LDFLAGS", & @@ -233,8 +235,13 @@ subroutine get_command_line_settings(cmd_settings) case default ; os_type = "OS Type: UNKNOWN" end select unix = os_is_unix(os) + + ! Get current release version + version = fpm_version() + call version%to_string(version_s) + version_text = [character(len=80) :: & - & 'Version: 0.7.0, alpha', & + & 'Version: '//trim(version_s), & & 'Program: fpm(1)', & & 'Description: A Fortran package manager and build system', & & 'Home Page: https://github.com/fortran-lang/fpm', & From f0fa518a63f366d63538da778cd5fd18fb80a709 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 09:41:14 +0200 Subject: [PATCH 216/799] bump bootstrapper to 0.7.0 --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 297fe11514..b82adf61dd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -83,7 +83,7 @@ jobs: - name: Install fpm uses: fortran-lang/setup-fpm@v3 with: - fpm-version: 'v0.3.0' + fpm-version: 'v0.7.0' - name: Remove fpm from path shell: bash From 69626fefce2b4059dae5c325c82a545762aeb45b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 09:42:31 +0200 Subject: [PATCH 217/799] bump bootstrapper to 0.7.0 in release --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f9ebda901..c814dc7aa7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: - name: Install fpm uses: fortran-lang/setup-fpm@v3 with: - fpm-version: 'v0.4.0' + fpm-version: 'v0.7.0' - name: Create single file version run: | From 25c359e5b7e3a8a4ae047cf5ac220a7fd286d76f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 09:44:56 +0200 Subject: [PATCH 218/799] conflict: rename macro to FPM_RELEASE_VERSION --- fpm.toml | 2 +- src/fpm/fpm_release.f90 | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fpm.toml b/fpm.toml index 392e9a0b97..c016fd2d43 100644 --- a/fpm.toml +++ b/fpm.toml @@ -13,7 +13,7 @@ M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" [preprocess] [preprocess.cpp] -macros=["FPM_VERSION={version}"] +macros=["FPM_RELEASE_VERSION={version}"] [library] include-dir = "include" diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 09250be3b1..840726d67a 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -6,10 +6,10 @@ module fpm_release implicit none private -#ifndef FPM_VERSION -#define FPM_VERSION UNDEFINED +#ifndef FPM_RELEASE_VERSION +#define FPM_RELEASE_VERSION UNDEFINED #endif - character(len=*), parameter :: fpm_version_ID = "FPM_VERSION" + character(len=*), parameter :: fpm_version_ID = "FPM_RELEASE_VERSION" public :: fpm_version public :: version_t From ef6532b434e577109c639e442d06dc65f668dd1a Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Fri, 7 Apr 2023 09:53:13 +0200 Subject: [PATCH 219/799] Add support for toggling Fortran features (#864) - implicit-typing: toggle default implicit typing rules - option to disable in GFortran - option to enable in LFortran - implicit-external: toggle implicit external interfaces - option to disable in GFortran - option to enable in LFortran - source-form: select source form ("free", "fixed", or "default") - option to set free/fixed form in GFortran - option to set fixed form in LFortran --- ci/run_tests.sh | 8 + example_packages/fixed-form/app/main.f90 | 4 + example_packages/fixed-form/fpm.toml | 2 + example_packages/fixed-form/src/lib.f90 | 7 + example_packages/free-form/app/main.f | 4 + example_packages/free-form/fpm.toml | 3 + example_packages/free-form/src/lib.f | 6 + .../implicit-external/app/main.f90 | 5 + example_packages/implicit-external/fpm.toml | 2 + .../implicit-external/src/impl.f90 | 4 + example_packages/implicit-typing/app/main.f90 | 4 + example_packages/implicit-typing/fpm.toml | 2 + example_packages/implicit-typing/src/impl.f90 | 3 + src/fpm.f90 | 7 +- src/fpm/manifest/fortran.f90 | 105 +++++++++++++ src/fpm/manifest/package.f90 | 15 +- src/fpm_compiler.F90 | 140 +++++++++++++++++- src/fpm_model.f90 | 21 ++- src/fpm_targets.f90 | 38 ++++- 19 files changed, 368 insertions(+), 12 deletions(-) create mode 100644 example_packages/fixed-form/app/main.f90 create mode 100644 example_packages/fixed-form/fpm.toml create mode 100644 example_packages/fixed-form/src/lib.f90 create mode 100644 example_packages/free-form/app/main.f create mode 100644 example_packages/free-form/fpm.toml create mode 100644 example_packages/free-form/src/lib.f create mode 100644 example_packages/implicit-external/app/main.f90 create mode 100644 example_packages/implicit-external/fpm.toml create mode 100644 example_packages/implicit-external/src/impl.f90 create mode 100644 example_packages/implicit-typing/app/main.f90 create mode 100644 example_packages/implicit-typing/fpm.toml create mode 100644 example_packages/implicit-typing/src/impl.f90 create mode 100644 src/fpm/manifest/fortran.f90 diff --git a/ci/run_tests.sh b/ci/run_tests.sh index ddbd3af9b2..45f45b6226 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -158,6 +158,14 @@ pushd cpp_files "$fpm" test popd +# Test Fortran features +for feature in free-form fixed-form implicit-typing implicit-external +do + pushd $feature + "$fpm" run + popd +done + # Test app exit codes pushd fpm_test_exit_code "$fpm" build diff --git a/example_packages/fixed-form/app/main.f90 b/example_packages/fixed-form/app/main.f90 new file mode 100644 index 0000000000..4524bbd8d0 --- /dev/null +++ b/example_packages/fixed-form/app/main.f90 @@ -0,0 +1,4 @@ + program test + use lib + call hello + end diff --git a/example_packages/fixed-form/fpm.toml b/example_packages/fixed-form/fpm.toml new file mode 100644 index 0000000000..26b8e4bb68 --- /dev/null +++ b/example_packages/fixed-form/fpm.toml @@ -0,0 +1,2 @@ +name = "fixed-form" +fortran.source-form = "fixed" diff --git a/example_packages/fixed-form/src/lib.f90 b/example_packages/fixed-form/src/lib.f90 new file mode 100644 index 0000000000..a2ed363db1 --- /dev/null +++ b/example_packages/fixed-form/src/lib.f90 @@ -0,0 +1,7 @@ + module lib + contains + subroutine h e l l o + print '(a)', + +"Hello, fixed world!" + end subroutine + end module diff --git a/example_packages/free-form/app/main.f b/example_packages/free-form/app/main.f new file mode 100644 index 0000000000..e2d305a049 --- /dev/null +++ b/example_packages/free-form/app/main.f @@ -0,0 +1,4 @@ +program test +use lib +call hello +end diff --git a/example_packages/free-form/fpm.toml b/example_packages/free-form/fpm.toml new file mode 100644 index 0000000000..c10afba2a6 --- /dev/null +++ b/example_packages/free-form/fpm.toml @@ -0,0 +1,3 @@ +name = "free-form" +fortran.source-form = "free" +executable = [{main="main.f", name="free-form"}] diff --git a/example_packages/free-form/src/lib.f b/example_packages/free-form/src/lib.f new file mode 100644 index 0000000000..520d6265c9 --- /dev/null +++ b/example_packages/free-form/src/lib.f @@ -0,0 +1,6 @@ +module lib +contains +subroutine hello +print '(a)', "Hello, free world!" +end subroutine +end module diff --git a/example_packages/implicit-external/app/main.f90 b/example_packages/implicit-external/app/main.f90 new file mode 100644 index 0000000000..5b6e8f6fc5 --- /dev/null +++ b/example_packages/implicit-external/app/main.f90 @@ -0,0 +1,5 @@ +program test + integer :: ijk + call impl(ijk) + if (ijk /= 1) error stop +end program test diff --git a/example_packages/implicit-external/fpm.toml b/example_packages/implicit-external/fpm.toml new file mode 100644 index 0000000000..c32145c56f --- /dev/null +++ b/example_packages/implicit-external/fpm.toml @@ -0,0 +1,2 @@ +name = "implicit-external" +fortran.implicit-external = true diff --git a/example_packages/implicit-external/src/impl.f90 b/example_packages/implicit-external/src/impl.f90 new file mode 100644 index 0000000000..1b609f561f --- /dev/null +++ b/example_packages/implicit-external/src/impl.f90 @@ -0,0 +1,4 @@ +subroutine impl(ijk) + integer :: ijk + ijk = 1 +end subroutine impl diff --git a/example_packages/implicit-typing/app/main.f90 b/example_packages/implicit-typing/app/main.f90 new file mode 100644 index 0000000000..944d95ede3 --- /dev/null +++ b/example_packages/implicit-typing/app/main.f90 @@ -0,0 +1,4 @@ +program test + use impl + if (ijk /= 1) error stop +end program diff --git a/example_packages/implicit-typing/fpm.toml b/example_packages/implicit-typing/fpm.toml new file mode 100644 index 0000000000..fe5c635069 --- /dev/null +++ b/example_packages/implicit-typing/fpm.toml @@ -0,0 +1,2 @@ +name = "implicit-typing" +fortran.implicit-typing = true diff --git a/example_packages/implicit-typing/src/impl.f90 b/example_packages/implicit-typing/src/impl.f90 new file mode 100644 index 0000000000..1803cb3cc9 --- /dev/null +++ b/example_packages/implicit-typing/src/impl.f90 @@ -0,0 +1,3 @@ +module impl + parameter(ijk = 1) +end module diff --git a/src/fpm.f90 b/src/fpm.f90 index 51a1bb16f5..26d85c49f6 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -10,7 +10,7 @@ module fpm use fpm_environment, only: get_env use fpm_filesystem, only: is_dir, join_path, list_files, exists, & basename, filewrite, mkdir, run, os_delete_dir -use fpm_model, only: fpm_model_t, srcfile_t, show_model, & +use fpm_model, only: fpm_model_t, srcfile_t, show_model, fortran_features_t, & FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, FPM_SCOPE_DEP, & FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST use fpm_compiler, only: new_compiler, new_archiver, set_cpp_preprocessor_flags @@ -112,6 +112,11 @@ subroutine build_model(model, settings, package, error) if (allocated(error)) exit model%packages(i)%name = dependency%name + associate(features => model%packages(i)%features) + features%implicit_typing = dependency%fortran%implicit_typing + features%implicit_external = dependency%fortran%implicit_external + features%source_form = dependency%fortran%source_form + end associate call package%version%to_string(version) model%packages(i)%version = version diff --git a/src/fpm/manifest/fortran.f90 b/src/fpm/manifest/fortran.f90 new file mode 100644 index 0000000000..bf76fa2e38 --- /dev/null +++ b/src/fpm/manifest/fortran.f90 @@ -0,0 +1,105 @@ +module fpm_manifest_fortran + use fpm_error, only : error_t, syntax_error, fatal_error + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + implicit none + private + + public :: fortran_config_t, new_fortran_config + + !> Configuration data for Fortran + type :: fortran_config_t + + !> Enable default implicit typing + logical :: implicit_typing + + !> Enable implicit external interfaces + logical :: implicit_external + + !> Form to use for all Fortran sources + character(:), allocatable :: source_form + + end type fortran_config_t + +contains + + !> Construct a new build configuration from a TOML data structure + subroutine new_fortran_config(self, table, error) + + !> Instance of the fortran configuration + type(fortran_config_t), intent(out) :: self + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: stat + character(:), allocatable :: source_form + + call check(table, error) + if (allocated(error)) return + + call get_value(table, "implicit-typing", self%implicit_typing, .false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'implicit-typing' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "implicit-external", self%implicit_external, .false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'implicit-external' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "source-form", source_form, "free", stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'source-form' in fpm.toml, expecting logical") + return + end if + select case(source_form) + case default + call fatal_error(error,"Value of source-form cannot be '"//source_form//"'") + return + case("free", "fixed", "default") + self%source_form = source_form + end select + + end subroutine new_fortran_config + + !> Check local schema for allowed entries + subroutine check(table, error) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_key), allocatable :: list(:) + integer :: ikey + + call table%get_keys(list) + + ! table can be empty + if (size(list) < 1) return + + do ikey = 1, size(list) + select case(list(ikey)%key) + + case("implicit-typing", "implicit-external", "source-form") + continue + + case default + call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in fortran") + exit + + end select + end do + + end subroutine check + +end module fpm_manifest_fortran diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index de124a0b3e..e966bfa461 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -27,6 +27,7 @@ !>[profiles] !>[build] !>[install] +!>[fortran] !>[[ executable ]] !>[[ example ]] !>[[ test ]] @@ -38,6 +39,7 @@ module fpm_manifest_package use fpm_manifest_profile, only : profile_config_t, new_profiles, get_default_profiles use fpm_manifest_example, only : example_config_t, new_example use fpm_manifest_executable, only : executable_config_t, new_executable + use fpm_manifest_fortran, only : fortran_config_t, new_fortran_config use fpm_manifest_library, only : library_config_t, new_library use fpm_manifest_install, only: install_config_t, new_install_config use fpm_manifest_test, only : test_config_t, new_test @@ -75,6 +77,9 @@ module fpm_manifest_package !> Installation configuration data type(install_config_t) :: install + !> Fortran meta data + type(fortran_config_t) :: fortran + !> Library meta data type(library_config_t), allocatable :: library @@ -173,6 +178,14 @@ subroutine new_package(self, table, root, error) call new_install_config(self%install, child, error) if (allocated(error)) return + call get_value(table, "fortran", child, requested=.true., stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Type mismatch for fortran entry, must be a table") + return + end if + call new_fortran_config(self%fortran, child, error) + if (allocated(error)) return + call get_value(table, "version", version, "0") call new_version(self%version, version, error) if (allocated(error) .and. present(root)) then @@ -328,7 +341,7 @@ subroutine check(table, error) case("version", "license", "author", "maintainer", "copyright", & & "description", "keywords", "categories", "homepage", "build", & & "dependencies", "dev-dependencies", "profiles", "test", "executable", & - & "example", "library", "install", "extra", "preprocess") + & "example", "library", "install", "extra", "preprocess", "fortran") continue end select diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index dee49f9f90..68b9c4af96 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -94,6 +94,8 @@ module fpm_compiler procedure :: get_module_flag !> Get flag for include directories procedure :: get_include_flag + !> Get feature flag + procedure :: get_feature_flag !> Compile a Fortran object procedure :: compile_fortran !> Compile a C object @@ -137,17 +139,23 @@ module fpm_compiler flag_gnu_opt = " -O3 -funroll-loops", & flag_gnu_debug = " -g", & flag_gnu_pic = " -fPIC", & - flag_gnu_warn = " -Wall -Wextra -Wimplicit-interface", & + flag_gnu_warn = " -Wall -Wextra", & flag_gnu_check = " -fcheck=bounds -fcheck=array-temps", & flag_gnu_limit = " -fmax-errors=1", & - flag_gnu_external = " -Wimplicit-interface" + flag_gnu_external = " -Wimplicit-interface", & + flag_gnu_no_implicit_typing = " -fimplicit-none", & + flag_gnu_no_implicit_external = " -Werror=implicit-interface", & + flag_gnu_free_form = " -ffree-form", & + flag_gnu_fixed_form = " -ffixed-form" character(*), parameter :: & flag_pgi_backslash = " -Mbackslash", & flag_pgi_traceback = " -traceback", & flag_pgi_debug = " -g", & flag_pgi_check = " -Mbounds -Mchkptr -Mchkstk", & - flag_pgi_warn = " -Minform=inform" + flag_pgi_warn = " -Minform=inform", & + flag_pgi_free_form = " -Mfree", & + flag_pgi_fixed_form = " -Mfixed" character(*), parameter :: & flag_ibmxl_backslash = " -qnoescape" @@ -162,7 +170,9 @@ module fpm_compiler flag_intel_limit = " -error-limit 1", & flag_intel_pthread = " -reentrancy threaded", & flag_intel_nogen = " -nogen-interfaces", & - flag_intel_byterecl = " -assume byterecl" + flag_intel_byterecl = " -assume byterecl", & + flag_intel_free_form = " -free", & + flag_intel_fixed_form = " -fixed" character(*), parameter :: & flag_intel_backtrace_win = " /traceback", & @@ -174,7 +184,9 @@ module fpm_compiler flag_intel_limit_win = " /error-limit:1", & flag_intel_pthread_win = " /reentrancy:threaded", & flag_intel_nogen_win = " /nogen-interfaces", & - flag_intel_byterecl_win = " /assume:byterecl" + flag_intel_byterecl_win = " /assume:byterecl", & + flag_intel_free_form_win = " /free", & + flag_intel_fixed_form_win = " /fixed" character(*), parameter :: & flag_nag_coarray = " -coarray=single", & @@ -182,11 +194,23 @@ module fpm_compiler flag_nag_check = " -C", & flag_nag_debug = " -g -O0", & flag_nag_opt = " -O4", & - flag_nag_backtrace = " -gline" + flag_nag_backtrace = " -gline", & + flag_nag_free_form = " -free", & + flag_nag_fixed_form = " -fixed", & + flag_nag_no_implicit_typing = " -u" character(*), parameter :: & - flag_lfortran_opt = " --fast" + flag_lfortran_opt = " --fast", & + flag_lfortran_implicit_typing = " --implicit-typing", & + flag_lfortran_implicit_external = " --allow-implicit-interface", & + flag_lfortran_fixed_form = " --fixed-form" + +character(*), parameter :: & + flag_cray_no_implicit_typing = " -dl", & + flag_cray_implicit_typing = " -el", & + flag_cray_fixed_form = " -ffixed", & + flag_cray_free_form = " -ffree" contains @@ -539,6 +563,108 @@ function get_module_flag(self, path) result(flags) end function get_module_flag +function get_feature_flag(self, feature) result(flags) + class(compiler_t), intent(in) :: self + character(len=*), intent(in) :: feature + character(len=:), allocatable :: flags + + flags = "" + select case(feature) + case("no-implicit-typing") + select case(self%id) + case(id_caf, id_gcc, id_f95) + flags = flag_gnu_no_implicit_typing + + case(id_nag) + flags = flag_nag_no_implicit_typing + + case(id_cray) + flags = flag_cray_no_implicit_typing + + end select + + case("implicit-typing") + select case(self%id) + case(id_cray) + flags = flag_cray_implicit_typing + + case(id_lfortran) + flags = flag_lfortran_implicit_typing + + end select + + case("no-implicit-external") + select case(self%id) + case(id_caf, id_gcc, id_f95) + flags = flag_gnu_no_implicit_external + + end select + + case("implicit-external") + select case(self%id) + case(id_lfortran) + flags = flag_lfortran_implicit_external + + end select + + case("free-form") + select case(self%id) + case(id_caf, id_gcc, id_f95) + flags = flag_gnu_free_form + + case(id_pgi, id_nvhpc, id_flang) + flags = flag_pgi_free_form + + case(id_nag) + flags = flag_nag_free_form + + case(id_intel_classic_nix, id_intel_classic_mac, id_intel_llvm_nix, & + & id_intel_llvm_unknown) + flags = flag_intel_free_form + + case(id_intel_classic_windows, id_intel_llvm_windows) + flags = flag_intel_free_form_win + + case(id_cray) + flags = flag_cray_free_form + + end select + + case("fixed-form") + select case(self%id) + case(id_caf, id_gcc, id_f95) + flags = flag_gnu_fixed_form + + case(id_pgi, id_nvhpc, id_flang) + flags = flag_pgi_fixed_form + + case(id_nag) + flags = flag_nag_fixed_form + + case(id_intel_classic_nix, id_intel_classic_mac, id_intel_llvm_nix, & + & id_intel_llvm_unknown) + flags = flag_intel_fixed_form + + case(id_intel_classic_windows, id_intel_llvm_windows) + flags = flag_intel_fixed_form_win + + case(id_cray) + flags = flag_cray_fixed_form + + case(id_lfortran) + flags = flag_lfortran_fixed_form + + end select + + case("default-form") + continue + + case default + error stop "Unknown feature '"//feature//"'" + end select +end function get_feature_flag + + subroutine get_default_c_compiler(f_compiler, c_compiler) character(len=*), intent(in) :: f_compiler character(len=:), allocatable, intent(out) :: c_compiler diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index c5fe38cc77..dba15a8161 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -42,7 +42,7 @@ module fpm_model implicit none private -public :: fpm_model_t, srcfile_t, show_model +public :: fpm_model_t, srcfile_t, show_model, fortran_features_t public :: FPM_UNIT_UNKNOWN, FPM_UNIT_PROGRAM, FPM_UNIT_MODULE, & FPM_UNIT_SUBMODULE, FPM_UNIT_SUBPROGRAM, FPM_UNIT_CSOURCE, & @@ -79,6 +79,18 @@ module fpm_model integer, parameter :: FPM_SCOPE_TEST = 4 integer, parameter :: FPM_SCOPE_EXAMPLE = 5 +!> Enabled Fortran language features +type :: fortran_features_t + + !> Use default implicit typing + logical :: implicit_typing = .false. + + !> Use implicit external interface + logical :: implicit_external = .false. + + !> Form to use for all Fortran sources + character(:), allocatable :: source_form +end type fortran_features_t !> Type for describing a source file type srcfile_t @@ -132,8 +144,13 @@ module fpm_model !> Module naming conventions logical :: enforce_module_names + + !> Prefix for all module names type(string_t) :: module_prefix + !> Language features + type(fortran_features_t) :: features + end type package_t @@ -185,6 +202,8 @@ module fpm_model !> Whether module names should be prefixed with the package name logical :: enforce_module_names = .false. + + !> Prefix for all module names type(string_t) :: module_prefix end type fpm_model_t diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index c01cd4ee15..e7ec525c09 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -27,6 +27,7 @@ module fpm_targets use iso_fortran_env, only: int64 use fpm_error, only: error_t, fatal_error, fpm_stop use fpm_model +use fpm_compiler, only : compiler_t use fpm_environment, only: get_os_type, OS_WINDOWS, OS_MACOS use fpm_filesystem, only: dirname, join_path, canon_path use fpm_strings, only: string_t, operator(.in.), string_cat, fnv_1a, resize, lower, str_ends_with @@ -114,6 +115,9 @@ module fpm_targets !> Flag set if build target will be skipped (not built) logical :: skip = .false. + !> Language features + type(fortran_features_t) :: features + !> Targets in the same schedule group are guaranteed to be independent integer :: schedule = -1 @@ -233,6 +237,7 @@ subroutine build_target_list(targets,model) type = merge(FPM_TARGET_C_OBJECT,FPM_TARGET_OBJECT,& sources(i)%unit_type==FPM_UNIT_CSOURCE), & output_name = get_object_name(sources(i)), & + features = model%packages(j)%features, & macros = model%packages(j)%macros, & version = model%packages(j)%version) @@ -279,6 +284,7 @@ subroutine build_target_list(targets,model) call add_target(targets,package=model%packages(j)%name,type = exe_type,& output_name = get_object_name(sources(i)), & source = sources(i), & + features = model%packages(j)%features, & macros = model%packages(j)%macros & ) @@ -397,13 +403,15 @@ end subroutine collect_exe_link_dependencies !> Allocate a new target and append to target list -subroutine add_target(targets,package,type,output_name,source,link_libraries, macros, version) +subroutine add_target(targets, package, type, output_name, source, link_libraries, & + & features, macros, version) type(build_target_ptr), allocatable, intent(inout) :: targets(:) character(*), intent(in) :: package integer, intent(in) :: type character(*), intent(in) :: output_name type(srcfile_t), intent(in), optional :: source type(string_t), intent(in), optional :: link_libraries(:) + type(fortran_features_t), intent(in), optional :: features type(string_t), intent(in), optional :: macros(:) character(*), intent(in), optional :: version @@ -432,6 +440,7 @@ subroutine add_target(targets,package,type,output_name,source,link_libraries, ma new_target%package_name = package if (present(source)) new_target%source = source if (present(link_libraries)) new_target%link_libraries = link_libraries + if (present(features)) new_target%features = features if (present(macros)) new_target%macros = macros if (present(version)) new_target%version = version allocate(new_target%dependencies(0)) @@ -801,7 +810,8 @@ subroutine resolve_target_linking(targets, model) associate(target => targets(i)%ptr) if (target%target_type /= FPM_TARGET_C_OBJECT .and. target%target_type /= FPM_TARGET_CPP_OBJECT) then - target%compile_flags = model%fortran_compile_flags + target%compile_flags = model%fortran_compile_flags & + & // get_feature_flags(model%compiler, target%features) else if (target%target_type == FPM_TARGET_C_OBJECT) then target%compile_flags = model%c_compile_flags else if(target%target_type == FPM_TARGET_CPP_OBJECT) then @@ -1029,4 +1039,28 @@ subroutine filter_modules(targets, list) end subroutine filter_modules +function get_feature_flags(compiler, features) result(flags) + type(compiler_t), intent(in) :: compiler + type(fortran_features_t), intent(in) :: features + character(:), allocatable :: flags + + flags = "" + if (features%implicit_typing) then + flags = flags // compiler%get_feature_flag("implicit-typing") + else + flags = flags // compiler%get_feature_flag("no-implicit-typing") + end if + + if (features%implicit_external) then + flags = flags // compiler%get_feature_flag("implicit-external") + else + flags = flags // compiler%get_feature_flag("no-implicit-external") + end if + + if (allocated(features%source_form)) then + flags = flags // compiler%get_feature_flag(features%source_form//"-form") + end if +end function get_feature_flags + + end module fpm_targets From 9c853f559f0f3a40c8d24b42b70f8c8f2c65edc3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 09:55:21 +0200 Subject: [PATCH 220/799] move preprocess up --- fpm.toml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fpm.toml b/fpm.toml index c016fd2d43..b524840fe4 100644 --- a/fpm.toml +++ b/fpm.toml @@ -5,19 +5,16 @@ author = "fpm maintainers" maintainer = "" copyright = "2020 fpm contributors" +[preprocess] +[preprocess.cpp] +macros=["FPM_RELEASE_VERSION={version}"] + [dependencies] toml-f.git = "https://github.com/toml-f/toml-f" toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" -[preprocess] -[preprocess.cpp] -macros=["FPM_RELEASE_VERSION={version}"] - -[library] -include-dir = "include" - [[test]] name = "cli-test" source-dir = "test/cli_test" @@ -37,3 +34,4 @@ main = "main.f90" name = "help-test" source-dir = "test/help_test" main = "help_test.f90" + From b3e1327140b9cdde874e2879e6ad07dd096447d1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 10:00:40 +0200 Subject: [PATCH 221/799] add verbosity --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b82adf61dd..b443444a24 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -94,7 +94,7 @@ jobs: - name: Build Fortran fpm (bootstrap) shell: bash run: | - ${{ env.BOOTSTRAP }} build + ${{ env.BOOTSTRAP }} build --verbose - name: Run Fortran fpm (bootstrap) shell: bash From 87ac559175a390a1d1ecbec84c06486f520e530f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 10:24:41 +0200 Subject: [PATCH 222/799] update bootstrapping version to 0.8.0 --- .github/workflows/CI.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b443444a24..7cdd8dc4eb 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -83,7 +83,7 @@ jobs: - name: Install fpm uses: fortran-lang/setup-fpm@v3 with: - fpm-version: 'v0.7.0' + fpm-version: 'v0.8.0' - name: Remove fpm from path shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c814dc7aa7..13a08cbe2d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: - name: Install fpm uses: fortran-lang/setup-fpm@v3 with: - fpm-version: 'v0.7.0' + fpm-version: 'v0.8.0' - name: Create single file version run: | From 2a6972731c5630c3f3210001496874af55f6af86 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Fri, 7 Apr 2023 13:03:50 +0200 Subject: [PATCH 223/799] Release version 0.8.0 (#869) --- fpm.toml | 2 +- src/fpm_command_line.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index 413c21b817..e59941e417 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.7.0" +version = "0.8.0" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 985d8892a2..4a434deb4b 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -235,7 +235,7 @@ subroutine get_command_line_settings(cmd_settings) end select unix = os_is_unix(os) version_text = [character(len=80) :: & - & 'Version: 0.7.0, alpha', & + & 'Version: 0.8.0, alpha', & & 'Program: fpm(1)', & & 'Description: A Fortran package manager and build system', & & 'Home Page: https://github.com/fortran-lang/fpm', & From 99fde02ec4587c4c33424f5176e9d301df183de9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 13:31:09 +0200 Subject: [PATCH 224/799] update version_t api --- src/fpm_command_line.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 192b60e0b1..e0614ffcf8 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -239,7 +239,7 @@ subroutine get_command_line_settings(cmd_settings) ! Get current release version version = fpm_version() - call version%to_string(version_s) + version_s = version%s() version_text = [character(len=80) :: & & 'Version: '//trim(version_s), & From e2f8aabc6ab59756eff91807c21b44339c4d9495 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:20:04 +0200 Subject: [PATCH 225/799] pass macro via traditional-cpp (gfortran) --- src/fpm/fpm_release.f90 | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 840726d67a..509eb8ff5d 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -6,11 +6,6 @@ module fpm_release implicit none private -#ifndef FPM_RELEASE_VERSION -#define FPM_RELEASE_VERSION UNDEFINED -#endif - character(len=*), parameter :: fpm_version_ID = "FPM_RELEASE_VERSION" - public :: fpm_version public :: version_t @@ -21,6 +16,27 @@ type(version_t) function fpm_version() type(error_t), allocatable :: error + ! Accept solution from https://stackoverflow.com/questions/31649691/stringify-macro-with-gnu-gfortran + ! which provides the "easiest" way to pass a macro to a string in Fortran complying with both + ! gfortran's "traditional" cpp and the standard cpp syntaxes + +#ifndef FPM_RELEASE_VERSION +# define FPM_RELEASE_VERSION UNDEFINED +#endif + +#ifdef __GFORTRAN__ /* traditional-cpp stringification */ +# define STRINGIFY_START(X) "& +# define STRINGIFY_END(X) &X" +#else /* default stringification */ +# define STRINGIFY_(X) #X +# define STRINGIFY_START(X) & +# define STRINGIFY_END(X) STRINGIFY_(X) +#endif + + character (len=:), allocatable :: ver_string + ver_string = STRINGIFY_START(FPM_RELEASE_VERSION) + STRINGIFY_END(FPM_RELEASE_VERSION) + call new_version(fpm_version,fpm_version_ID,error) if (allocated(error)) call fpm_stop(1,'*fpm*:internal error: cannot get version - '//error%message) From ccf98cb628a35ff8a942267e3b64a4b88ba84b2b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:22:44 +0200 Subject: [PATCH 226/799] fix ver_string --- src/fpm/fpm_release.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 509eb8ff5d..925868d172 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -37,7 +37,7 @@ type(version_t) function fpm_version() ver_string = STRINGIFY_START(FPM_RELEASE_VERSION) STRINGIFY_END(FPM_RELEASE_VERSION) - call new_version(fpm_version,fpm_version_ID,error) + call new_version(fpm_version,ver_string,error) if (allocated(error)) call fpm_stop(1,'*fpm*:internal error: cannot get version - '//error%message) From 1a4048fd3bcc51b4d410f1be69a86c24bb7d6d50 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:32:12 +0200 Subject: [PATCH 227/799] remove tests from manifest --- fpm.toml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/fpm.toml b/fpm.toml index 51faa0e94c..273e8e0ee7 100644 --- a/fpm.toml +++ b/fpm.toml @@ -17,23 +17,5 @@ M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" jonquil.git = "https://github.com/toml-f/jonquil" jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" -[[test]] -name = "cli-test" -source-dir = "test/cli_test" -main = "cli_test.f90" - -[[test]] -name = "new-test" -source-dir = "test/new_test" -main = "new_test.f90" - -[[test]] -name = "fpm-test" -source-dir = "test/fpm_test" -main = "main.f90" - -[[test]] -name = "help-test" -source-dir = "test/help_test" -main = "help_test.f90" - +[build] +auto-tests = false From c274c68e569d324deca837fca533cec5a486b117 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:33:03 +0200 Subject: [PATCH 228/799] restore tests; update single-file patch --- ci/single-file.patch | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ci/single-file.patch b/ci/single-file.patch index 7980a7a663..9817638dd3 100644 --- a/ci/single-file.patch +++ b/ci/single-file.patch @@ -1,10 +1,10 @@ diff --git a/fpm.toml b/fpm.toml -index 12ec05aa..20425dfd 100644 +index 51faa0e9..273e8e0e 100644 --- a/fpm.toml +++ b/fpm.toml -@@ -14,22 +14,5 @@ rev = "2f5eaba864ff630ba0c3791126a3f811b6e437f3" - git = "https://github.com/urbanjost/M_CLI2.git" - rev = "ea6bbffc1c2fb0885e994d37ccf0029c99b19f24" +@@ -17,23 +17,5 @@ M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" + jonquil.git = "https://github.com/toml-f/jonquil" + jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" -[[test]] -name = "cli-test" @@ -25,5 +25,6 @@ index 12ec05aa..20425dfd 100644 -name = "help-test" -source-dir = "test/help_test" -main = "help_test.f90" +- +[build] +auto-tests = false From 497e43df8a383c3c7a20b43582ca6c351e9b0b66 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:33:50 +0200 Subject: [PATCH 229/799] restore tests --- fpm.toml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index 273e8e0ee7..51faa0e94c 100644 --- a/fpm.toml +++ b/fpm.toml @@ -17,5 +17,23 @@ M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" jonquil.git = "https://github.com/toml-f/jonquil" jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" -[build] -auto-tests = false +[[test]] +name = "cli-test" +source-dir = "test/cli_test" +main = "cli_test.f90" + +[[test]] +name = "new-test" +source-dir = "test/new_test" +main = "new_test.f90" + +[[test]] +name = "fpm-test" +source-dir = "test/fpm_test" +main = "main.f90" + +[[test]] +name = "help-test" +source-dir = "test/help_test" +main = "help_test.f90" + From 30ee35944617f93be89cf82be225699bf07bca81 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:37:01 +0200 Subject: [PATCH 230/799] add verbose to release CI --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13a08cbe2d..9ab4feb125 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,7 +92,7 @@ jobs: - name: Build fpm with bootstrap version run: | - ${{ env.EXE }} build + ${{ env.EXE }} build --verbose - name: Upload artifact uses: actions/upload-artifact@v2 From d494c09d5c586acccb955f67ddb7a54f12ff538d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:43:26 +0200 Subject: [PATCH 231/799] single-file build is not made with fpm! pass macro manually --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ab4feb125..cb83fcab21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,6 +71,7 @@ jobs: # Intel compiler predefines "linux" and "unix" which causes inadvertent substitutions echo "#undef linux" >> fpm-${{ env.VERSION }}.F90 echo "#undef unix" >> fpm-${{ env.VERSION }}.F90 + echo "#define FPM_RELEASE_VERSION ${{ env.VERSION }}" fpm build --compiler ./ci/single-file-gfortran.sh env: OUTPUT: fpm-${{ env.VERSION }}.F90 From 5c3c00150c705d314a0bc7a18b5feffcfa5e9340 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 15:47:57 +0200 Subject: [PATCH 232/799] pass version via macro --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cb83fcab21..4e49fe2da3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,10 +68,10 @@ jobs: run: | echo "OUTPUT=${{ env.OUTPUT }}" >> $GITHUB_ENV echo "#define FPM_BOOTSTRAP" > fpm-${{ env.VERSION }}.F90 + echo "#define FPM_RELEASE_VERSION ${{ env.VERSION }}" >> fpm-${{ env.VERSION }}.F90 # Intel compiler predefines "linux" and "unix" which causes inadvertent substitutions echo "#undef linux" >> fpm-${{ env.VERSION }}.F90 echo "#undef unix" >> fpm-${{ env.VERSION }}.F90 - echo "#define FPM_RELEASE_VERSION ${{ env.VERSION }}" fpm build --compiler ./ci/single-file-gfortran.sh env: OUTPUT: fpm-${{ env.VERSION }}.F90 From 5670355b3f72b954d24b46f9fe83d56481205af3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 16:01:05 +0200 Subject: [PATCH 233/799] Update release CI with manifest version --- .github/workflows/release.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e49fe2da3..a2bb4c37cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,6 +52,13 @@ jobs: run: | VERSION=$(git describe --tags --match 'v*' --always | tr -d 'v') echo "VERSION=${VERSION}" >> $GITHUB_ENV + echo ${{ env.VERSION }} + + - name: Get manifest version + run: | + MANIFEST_VERSION=$(grep version fpm.toml | head -1 | tr -d ' ' | tr -d 'version=') + echo "MANIFEST_VERSION=${MANIFEST_VERSION}" >> $GITHUB_ENV + echo ${{ env.MANIFEST_VERSION }} # Note: this step is meant to remove the test targets from the package manifest, # a change in the package manifest might require to regenerate the patch file. @@ -68,7 +75,8 @@ jobs: run: | echo "OUTPUT=${{ env.OUTPUT }}" >> $GITHUB_ENV echo "#define FPM_BOOTSTRAP" > fpm-${{ env.VERSION }}.F90 - echo "#define FPM_RELEASE_VERSION ${{ env.VERSION }}" >> fpm-${{ env.VERSION }}.F90 + # We need to pass the exact version string that a fpm build command would send + echo "#define FPM_RELEASE_VERSION ${{ env.MANIFEST_VERSION }}" > fpm-${{ env.VERSION }}.F90 # Intel compiler predefines "linux" and "unix" which causes inadvertent substitutions echo "#undef linux" >> fpm-${{ env.VERSION }}.F90 echo "#undef unix" >> fpm-${{ env.VERSION }}.F90 From c6cfc5e059a2e0a0df198d954768369df2734763 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 16:18:34 +0200 Subject: [PATCH 234/799] fix typo --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2bb4c37cd..f770987fe8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,7 +76,7 @@ jobs: echo "OUTPUT=${{ env.OUTPUT }}" >> $GITHUB_ENV echo "#define FPM_BOOTSTRAP" > fpm-${{ env.VERSION }}.F90 # We need to pass the exact version string that a fpm build command would send - echo "#define FPM_RELEASE_VERSION ${{ env.MANIFEST_VERSION }}" > fpm-${{ env.VERSION }}.F90 + echo "#define FPM_RELEASE_VERSION ${{ env.MANIFEST_VERSION }}" >> fpm-${{ env.VERSION }}.F90 # Intel compiler predefines "linux" and "unix" which causes inadvertent substitutions echo "#undef linux" >> fpm-${{ env.VERSION }}.F90 echo "#undef unix" >> fpm-${{ env.VERSION }}.F90 From ee473eecb652f7e662b58ccbbb254bc4193c2650 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 16:20:46 +0200 Subject: [PATCH 235/799] cleanup: remove `--verbose` flags --- .github/workflows/CI.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7cdd8dc4eb..a5aa8be35a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -94,7 +94,7 @@ jobs: - name: Build Fortran fpm (bootstrap) shell: bash run: | - ${{ env.BOOTSTRAP }} build --verbose + ${{ env.BOOTSTRAP }} build - name: Run Fortran fpm (bootstrap) shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2bb4c37cd..c1b53f8496 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -101,7 +101,7 @@ jobs: - name: Build fpm with bootstrap version run: | - ${{ env.EXE }} build --verbose + ${{ env.EXE }} build - name: Upload artifact uses: actions/upload-artifact@v2 From d95cf15313d15fab297022286eb6333318c5898c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Apr 2023 16:25:50 +0200 Subject: [PATCH 236/799] fallback to a known version if catastrophic error --- src/fpm/fpm_release.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.f90 index 925868d172..e80050b3ee 100644 --- a/src/fpm/fpm_release.f90 +++ b/src/fpm/fpm_release.f90 @@ -16,14 +16,14 @@ type(version_t) function fpm_version() type(error_t), allocatable :: error - ! Accept solution from https://stackoverflow.com/questions/31649691/stringify-macro-with-gnu-gfortran - ! which provides the "easiest" way to pass a macro to a string in Fortran complying with both - ! gfortran's "traditional" cpp and the standard cpp syntaxes - +! Fallback to last known version in case of undefined macro #ifndef FPM_RELEASE_VERSION -# define FPM_RELEASE_VERSION UNDEFINED +# define FPM_RELEASE_VERSION 0.8.0 #endif +! Accept solution from https://stackoverflow.com/questions/31649691/stringify-macro-with-gnu-gfortran +! which provides the "easiest" way to pass a macro to a string in Fortran complying with both +! gfortran's "traditional" cpp and the standard cpp syntaxes #ifdef __GFORTRAN__ /* traditional-cpp stringification */ # define STRINGIFY_START(X) "& # define STRINGIFY_END(X) &X" From 28e1e5f0d895dc27ace7d03e6b91770dfe323a13 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 01:24:45 -0500 Subject: [PATCH 237/799] Only update dependencies between cached build and manifest (#871) * only update dependencies that were cached * add test to CI * update dependency tree test * simplify CI * build separate `cache` and `project` trees, then compare dependencies * update cached deps test * improve comment * test dependency that should not be updated --- ci/run_tests.sh | 4 + .../dependency_priority/README.md | 4 + .../dependency_priority/app/main.f90 | 14 +++ example_packages/dependency_priority/fpm.toml | 12 ++ src/fpm/dependency.f90 | 59 ++++++--- src/fpm/git.f90 | 20 ++- src/fpm/manifest/dependency.f90 | 14 +-- test/fpm_test/test_package_dependencies.f90 | 116 ++++++++++++++++-- 8 files changed, 202 insertions(+), 41 deletions(-) create mode 100644 example_packages/dependency_priority/README.md create mode 100644 example_packages/dependency_priority/app/main.f90 create mode 100644 example_packages/dependency_priority/fpm.toml diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 45f45b6226..eb360485f6 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -201,6 +201,10 @@ EXIT_CODE=0 test $EXIT_CODE -eq 1 popd +# test dependency priority +pushd dependency_priority +"$fpm" run +popd # Cleanup rm -rf ./*/build diff --git a/example_packages/dependency_priority/README.md b/example_packages/dependency_priority/README.md new file mode 100644 index 0000000000..58038a2f98 --- /dev/null +++ b/example_packages/dependency_priority/README.md @@ -0,0 +1,4 @@ +# dependency_tree +Check dependency tree cascade. "Standard" fpm dependencies feature that the highest-priority one wins +(i.e., top-level dependencies in the manifest, or the first time it's found down the dependency tree) +Check this behavior is confirmed. diff --git a/example_packages/dependency_priority/app/main.f90 b/example_packages/dependency_priority/app/main.f90 new file mode 100644 index 0000000000..4f68826031 --- /dev/null +++ b/example_packages/dependency_priority/app/main.f90 @@ -0,0 +1,14 @@ +program main + use tomlf_version, only: tomlf_version_string + implicit none + + print *, 'using version =',tomlf_version_string + print *, 'should be =0.3.1' + + if (tomlf_version_string=="0.3.1") then + stop 0 + else + stop 1 + endif + +end program main diff --git a/example_packages/dependency_priority/fpm.toml b/example_packages/dependency_priority/fpm.toml new file mode 100644 index 0000000000..61dda2d4bd --- /dev/null +++ b/example_packages/dependency_priority/fpm.toml @@ -0,0 +1,12 @@ +name = "dependency_tree" +version = "0.1.0" +[build] +auto-executables=true +[dependencies] +# Request toml-f v0.3.1. +toml-f.git = "https://github.com/toml-f/toml-f" +toml-f.tag = "v0.3.1" +# jonquil 0.2.0 requires toml-f v0.4.0. +# Because 0.4.0 is a derived dependency, it should not be used +jonquil.git = "https://github.com/toml-f/jonquil" +jonquil.tag = "v0.2.0" diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 626b1d37a7..39574287ff 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -93,6 +93,8 @@ module fpm_dependency logical :: done = .false. !> Dependency should be updated logical :: update = .false. + !> Dependency was loaded from a cache + logical :: cached = .false. contains !> Update dependency from project manifest. procedure :: register @@ -284,12 +286,9 @@ subroutine add_project(self, package, error) type(error_t), allocatable, intent(out) :: error type(dependency_config_t) :: dependency + type(dependency_tree_t) :: cached character(len=*), parameter :: root = '.' - - if (allocated(self%cache)) then - call self%load(self%cache, error) - if (allocated(error)) return - end if + integer :: id if (.not. exists(self%dep_dir)) then call mkdir(self%dep_dir) @@ -309,6 +308,20 @@ subroutine add_project(self, package, error) call self%add(package, root, .true., error) if (allocated(error)) return + ! After resolving all dependencies, check if we have cached ones to avoid updates + if (allocated(self%cache)) then + call new_dependency_tree(cached, cache=self%cache) + call cached%load(self%cache, error) + if (allocated(error)) return + + ! Skip root node + do id=2,cached%ndep + cached%dep(id)%cached = .true. + call self%add(cached%dep(id), error) + if (allocated(error)) return + end do + end if + ! Now decent into the dependency tree, level for level do while (.not. self%finished()) call self%resolve(root, error) @@ -423,11 +436,19 @@ subroutine add_dependency_node(self, dependency, error) ! Check if it needs to be updated id = self%find(dependency%name) - ! Ensure an update is requested whenever the dependency has changed - if (dependency_has_changed(self%dep(id), dependency)) then - write (self%unit, out_fmt) "Dependency change detected:", dependency%name - self%dep(id) = dependency - self%dep(id)%update = .true. + ! If this dependency was in the cache, and we're now requesting a different version + ! in the manifest, ensure it is marked for update. Otherwise, if we're just querying + ! the same dependency from a lower branch of the dependency tree, the existing one from + ! the manifest has priority + if (dependency%cached) then + if (dependency_has_changed(dependency, self%dep(id))) then + write (self%unit, out_fmt) "Dependency change detected:", dependency%name + self%dep(id)%update = .true. + else + ! Store the cached one + self%dep(id) = dependency + self%dep(id)%update = .false. + endif end if else ! New dependency: add from scratch @@ -1161,26 +1182,26 @@ pure subroutine resize_dependency_node(var, n) end subroutine resize_dependency_node !> Check if a dependency node has changed - logical function dependency_has_changed(this, that) result(has_changed) + logical function dependency_has_changed(cached, manifest) result(has_changed) !> Two instances of the same dependency to be compared - type(dependency_node_t), intent(in) :: this, that + type(dependency_node_t), intent(in) :: cached, manifest has_changed = .true. !> All the following entities must be equal for the dependency to not have changed - if (manifest_has_changed(this, that)) return + if (manifest_has_changed(cached=cached, manifest=manifest)) return !> For now, only perform the following checks if both are available. A dependency in cache.toml !> will always have this metadata; a dependency from fpm.toml which has not been fetched yet !> may not have it - if (allocated(this%version) .and. allocated(that%version)) then - if (this%version /= that%version) return + if (allocated(cached%version) .and. allocated(manifest%version)) then + if (cached%version /= manifest%version) return end if - if (allocated(this%revision) .and. allocated(that%revision)) then - if (this%revision /= that%revision) return + if (allocated(cached%revision) .and. allocated(manifest%revision)) then + if (cached%revision /= manifest%revision) return end if - if (allocated(this%proj_dir) .and. allocated(that%proj_dir)) then - if (this%proj_dir /= that%proj_dir) return + if (allocated(cached%proj_dir) .and. allocated(manifest%proj_dir)) then + if (cached%proj_dir /= manifest%proj_dir) return end if !> All checks passed: the two dependencies have no differences diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 8825fbaca8..36b4702f0b 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -8,6 +8,7 @@ module fpm_git public :: git_target_default, git_target_branch, git_target_tag, & & git_target_revision public :: git_revision + public :: git_matches_manifest public :: operator(==) @@ -36,7 +37,7 @@ module fpm_git type :: git_target_t !> Kind of the git target - integer, private :: descriptor = git_descriptor%default + integer :: descriptor = git_descriptor%default !> Target URL of the git repository character(len=:), allocatable :: url @@ -145,6 +146,23 @@ logical function git_target_eq(this,that) result(is_equal) end function git_target_eq + !> Check that a cached dependency matches a manifest request + logical function git_matches_manifest(cached,manifest) + + !> Two input git targets + type(git_target_t), intent(in) :: cached,manifest + + git_matches_manifest = cached%url == manifest%url + if (.not.git_matches_manifest) return + + !> The manifest dependency only contains partial information (what's requested), + !> while the cached dependency always stores a commit hash because it's built + !> after the repo is available (saved as git_descriptor%revision==revision). + !> So, comparing against the descriptor is not reliable + git_matches_manifest = cached%object == manifest%object + + end function git_matches_manifest + subroutine checkout(self, local_path, error) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index ec40dfdf74..432137f655 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -25,7 +25,7 @@ module fpm_manifest_dependency use fpm_error, only: error_t, syntax_error use fpm_git, only: git_target_t, git_target_tag, git_target_branch, & - & git_target_revision, git_target_default, operator(==) + & git_target_revision, git_target_default, operator(==), git_matches_manifest use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS @@ -274,19 +274,17 @@ subroutine info(self, unit, verbosity) end subroutine info !> Check if two dependency configurations are different - logical function manifest_has_changed(this, that) result(has_changed) + logical function manifest_has_changed(cached, manifest) result(has_changed) !> Two instances of the dependency configuration - class(dependency_config_t), intent(in) :: this, that + class(dependency_config_t), intent(in) :: cached, manifest has_changed = .true. !> Perform all checks - if (this%name/=that%name) return - if (this%path/=that%path) return - if (allocated(this%git).neqv.allocated(that%git)) return - if (allocated(this%git)) then - if (.not.(this%git==that%git)) return + if (allocated(cached%git).neqv.allocated(manifest%git)) return + if (allocated(cached%git)) then + if (.not.git_matches_manifest(cached%git,manifest%git)) return end if !> All checks passed! The two instances are equal diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index e7600bc6b4..3c5b0ee021 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -45,6 +45,7 @@ subroutine collect_package_dependencies(tests) & new_unittest("status-after-load", test_status), & & new_unittest("add-dependencies", test_add_dependencies), & & new_unittest("update-dependencies", test_update_dependencies), & + & new_unittest("do-not-update-dependencies", test_non_updated_dependencies), & & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & & new_unittest("local-registry-specified-version-not-found", local_registry_specified_version_not_found, should_fail=.true.), & @@ -254,7 +255,7 @@ subroutine test_add_dependencies(error) end subroutine test_add_dependencies - subroutine test_update_dependencies(error) + subroutine test_non_updated_dependencies(error) !> Error handling type(error_t), allocatable, intent(out) :: error @@ -262,7 +263,7 @@ subroutine test_update_dependencies(error) type(toml_table) :: cache, manifest type(toml_table), pointer :: ptr type(toml_key), allocatable :: list(:) - type(dependency_tree_t) :: deps, cached_deps + type(dependency_tree_t) :: cached, manifest_deps integer :: ii ! Create a dummy cache @@ -283,11 +284,99 @@ subroutine test_update_dependencies(error) call set_value(ptr, "proj-dir", "fpm-tmp1-dir") ! Load into a dependency tree - call new_dependency_tree(cached_deps) - call cached_deps%load(cache, error) + call new_dependency_tree(cached) + call cached%load(cache, error) + if (allocated(error)) return + ! Mark all dependencies as "cached" + do ii=1,cached%ndep + cached%dep(ii)%cached = .true. + end do call cache%destroy() + + ! Create a dummy manifest, with different version + manifest = toml_table() + call add_table(manifest, "dep1", ptr) + call set_value(ptr, "version", "1.1.1") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(manifest, "dep2", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/lin4") + call set_value(ptr, "rev", "c0ffee") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(manifest, "dep3", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/pkg3") + call set_value(ptr, "rev", "t4a") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + + ! Load dependencies from manifest + call new_dependency_tree(manifest_deps) + call manifest_deps%load(manifest, error) + call manifest%destroy() if (allocated(error)) return + ! Add cached dependencies afterwards; will flag those that need udpate + do ii=1,cached%ndep + cached%dep(ii)%cached = .true. + call manifest_deps%add(cached%dep(ii), error) + if (allocated(error)) return + end do + + ! Test that dependencies 1-2 are flagged as "update" + if (.not. manifest_deps%dep(1)%update) then + call test_failed(error, "Updated dependency (different version) not detected") + return + end if + if (.not. manifest_deps%dep(2)%update) then + call test_failed(error, "Updated dependency (git address) not detected") + return + end if + + + ! Test that dependency 3 is flagged as "not update" + if (manifest_deps%dep(3)%update) then + call test_failed(error, "Updated dependency (git rev) detected, should not be") + return + end if + + end subroutine test_non_updated_dependencies + + subroutine test_update_dependencies(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: cache, manifest + type(toml_table), pointer :: ptr + type(toml_key), allocatable :: list(:) + type(dependency_tree_t) :: cached, manifest_deps + integer :: ii + + ! Create a dummy cache + cache = toml_table() + call add_table(cache, "dep1", ptr) + call set_value(ptr, "version", "1.1.0") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(cache, "dep2", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/lin2") + call set_value(ptr, "rev", "c0ffee") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(cache, "dep3", ptr) + call set_value(ptr, "git", "https://gitlab.com/fortran-lang/pkg3") + call set_value(ptr, "rev", "t4a") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + call add_table(cache, "dep4", ptr) + call set_value(ptr, "version", "1.0.0") + call set_value(ptr, "proj-dir", "fpm-tmp1-dir") + + ! Load into a dependency tree + call new_dependency_tree(cached) + call cached%load(cache, error) + if (allocated(error)) return + ! Mark all dependencies as "cached" + do ii=1,cached%ndep + cached%dep(ii)%cached = .true. + end do + call cache%destroy() + ! Create a dummy manifest, with different version manifest = toml_table() call add_table(manifest, "dep1", ptr) @@ -303,27 +392,28 @@ subroutine test_update_dependencies(error) call set_value(ptr, "proj-dir", "fpm-tmp1-dir") ! Load dependencies from manifest - call new_dependency_tree(deps) - call deps%load(manifest, error) + call new_dependency_tree(manifest_deps) + call manifest_deps%load(manifest, error) call manifest%destroy() if (allocated(error)) return - ! Add manifest dependencies - do ii = 1, cached_deps%ndep - call deps%add(cached_deps%dep(ii), error) - if (allocated(error)) return + ! Add cached dependencies afterwards; will flag those that need udpate + do ii=1,cached%ndep + cached%dep(ii)%cached = .true. + call manifest_deps%add(cached%dep(ii), error) + if (allocated(error)) return end do ! Test that all dependencies are flagged as "update" - if (.not. deps%dep(1)%update) then + if (.not. manifest_deps%dep(1)%update) then call test_failed(error, "Updated dependency (different version) not detected") return end if - if (.not. deps%dep(2)%update) then + if (.not. manifest_deps%dep(2)%update) then call test_failed(error, "Updated dependency (git address) not detected") return end if - if (.not. deps%dep(3)%update) then + if (.not. manifest_deps%dep(3)%update) then call test_failed(error, "Updated dependency (git rev) not detected") return end if From cd57284563a8177575ef507c9d1f182dd355357c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 09:10:32 +0200 Subject: [PATCH 238/799] Bump version in fpm.toml to 0.8.1 --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index e59941e417..de6c1b073f 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.8.0" +version = "0.8.1" license = "MIT" author = "fpm maintainers" maintainer = "" From 7b1327ab496dd02620572f39bb991bf7539a73e5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 09:16:28 +0200 Subject: [PATCH 239/799] Revert "Bump version in fpm.toml to 0.8.1" This reverts commit cd57284563a8177575ef507c9d1f182dd355357c. --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index de6c1b073f..e59941e417 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.8.1" +version = "0.8.0" license = "MIT" author = "fpm maintainers" maintainer = "" From d1db0176753c4d0e827d79eab6263e3b5d995669 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 09:29:22 +0200 Subject: [PATCH 240/799] print dependency update messages on verbosity>0 Make the verbosity of dependency update messages consistent with the rest of fpm --- src/fpm/dependency.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 39574287ff..08365c361d 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -442,7 +442,7 @@ subroutine add_dependency_node(self, dependency, error) ! the manifest has priority if (dependency%cached) then if (dependency_has_changed(dependency, self%dep(id))) then - write (self%unit, out_fmt) "Dependency change detected:", dependency%name + if (self%verbosity>0) write (self%unit, out_fmt) "Dependency change detected:", dependency%name self%dep(id)%update = .true. else ! Store the cached one @@ -496,7 +496,7 @@ subroutine update_dependency(self, name, error) associate (dep => self%dep(id)) if (allocated(dep%git) .and. dep%update) then - write (self%unit, out_fmt) "Update:", dep%name + if (self%verbosity>0) write (self%unit, out_fmt) "Update:", dep%name proj_dir = join_path(self%dep_dir, dep%name) call dep%git%checkout(proj_dir, error) if (allocated(error)) return From e30b6391b6c6c51b632655214d874a6dc07be7df Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 09:51:30 +0200 Subject: [PATCH 241/799] bump version to 0.8.1 --- fpm.toml | 2 +- src/fpm_command_line.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index e59941e417..de6c1b073f 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.8.0" +version = "0.8.1" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 4a434deb4b..fd9fd90ea9 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -235,7 +235,7 @@ subroutine get_command_line_settings(cmd_settings) end select unix = os_is_unix(os) version_text = [character(len=80) :: & - & 'Version: 0.8.0, alpha', & + & 'Version: 0.8.1, alpha', & & 'Program: fpm(1)', & & 'Description: A Fortran package manager and build system', & & 'Home Page: https://github.com/fortran-lang/fpm', & From 7673ef2525f2e78fe33ddfbc527dd1b4c3163c4e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 10:12:38 +0200 Subject: [PATCH 242/799] temporary: echo FPM and git versions in CI --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 297fe11514..e4e24ff891 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -135,6 +135,8 @@ jobs: VERSION=$(echo ${{ github.ref }} | cut -dv -f2) echo "VERSION=$VERSION" >> $GITHUB_ENV FPM_VERSION=$(${{ env.FPM }} --version | grep -o '${{ env.REGEX }}') + echo "git-based version = $VERSION" + echo "fpm-based version = $FPM_VERSION" [ "$VERSION" = "$FPM_VERSION" ] env: REGEX: '[0-9]\{1,4\}\.[0-9]\{1,4\}\.[0-9]\{1,4\}' From ce86ddaf8fdab5e7832dc9817a80b83483bc8b60 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 10:30:12 +0200 Subject: [PATCH 243/799] Revert "temporary: echo FPM and git versions in CI" This reverts commit 7673ef2525f2e78fe33ddfbc527dd1b4c3163c4e. --- .github/workflows/CI.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e4e24ff891..297fe11514 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -135,8 +135,6 @@ jobs: VERSION=$(echo ${{ github.ref }} | cut -dv -f2) echo "VERSION=$VERSION" >> $GITHUB_ENV FPM_VERSION=$(${{ env.FPM }} --version | grep -o '${{ env.REGEX }}') - echo "git-based version = $VERSION" - echo "fpm-based version = $FPM_VERSION" [ "$VERSION" = "$FPM_VERSION" ] env: REGEX: '[0-9]\{1,4\}\.[0-9]\{1,4\}\.[0-9]\{1,4\}' From d9c985171e8e84d1b0d4ee8b5eef2716064b3855 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 11:44:42 +0200 Subject: [PATCH 244/799] create serializable interface --- src/fpm/toml.f90 | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index f8d8ea2420..9320627240 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -25,8 +25,84 @@ module fpm_toml get_value, set_value, get_list, new_table, add_table, add_array, len, & toml_error, toml_serialize, toml_load, check_keys + !> An abstract interface for any fpm class that should be fully serializable to/from TOML/JSON + type, abstract, public :: fpm_serializable + + contains + + !> Dump to TOML table + procedure(fpm_to_toml), deferred :: dump_to_toml + + !> Dump TOML to unit/file + procedure, non_overridable :: dump_to_file + procedure, non_overridable :: dump_to_unit + + generic :: dump => dump_to_toml, dump_to_file, dump_to_unit + + end type fpm_serializable + + + abstract interface + + !> Write object to TOML datastructure + subroutine fpm_to_toml(self, table, error) + import fpm_serializable,toml_table,error_t + implicit none + + !> Instance of the dependency tree + class(fpm_serializable), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + end subroutine fpm_to_toml + + end interface + contains + + !> Write serializable object to a formatted Fortran unit + subroutine dump_to_unit(self, unit, error) + !> Instance of the dependency tree + class(fpm_serializable), intent(inout) :: self + !> Formatted unit + integer, intent(in) :: unit + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + + table = toml_table() + call self%dump(table, error) + + write (unit, '(a)') toml_serialize(table) + + call table%destroy() + + end subroutine dump_to_unit + + !> Write serializable object to file + subroutine dump_to_file(self, file, error) + !> Instance of the dependency tree + class(fpm_serializable), intent(inout) :: self + !> File name + character(len=*), intent(in) :: file + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: unit + + open (file=file, newunit=unit) + call self%dump(unit, error) + close (unit) + if (allocated(error)) return + + end subroutine dump_to_file + !> Process the configuration file to a TOML data structure subroutine read_package_file(table, manifest, error) From 71ffdb0cccee01406817bf75df16a22e6951264e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 13:07:58 +0200 Subject: [PATCH 245/799] base serialization api --- src/fpm/toml.f90 | 139 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 15 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 9320627240..ffd2a58d51 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -26,31 +26,41 @@ module fpm_toml toml_error, toml_serialize, toml_load, check_keys !> An abstract interface for any fpm class that should be fully serializable to/from TOML/JSON - type, abstract, public :: fpm_serializable + type, abstract, public :: serializable_t contains - !> Dump to TOML table - procedure(fpm_to_toml), deferred :: dump_to_toml + !> Dump to TOML table, unit, file + procedure(to_toml), deferred, private :: dump_to_toml + procedure, non_overridable, private :: dump_to_file + procedure, non_overridable, private :: dump_to_unit + generic :: dump => dump_to_toml, dump_to_file, dump_to_unit - !> Dump TOML to unit/file - procedure, non_overridable :: dump_to_file - procedure, non_overridable :: dump_to_unit + !> Load from TOML table, unit, file + procedure(from_toml), deferred, private :: load_from_toml + procedure, non_overridable, private :: load_from_file + procedure, non_overridable, private :: load_from_unit + generic :: load => load_from_toml, load_from_file, load_from_unit - generic :: dump => dump_to_toml, dump_to_file, dump_to_unit + !> Serializable entities need a way to check that they're equal + procedure(is_equal), deferred, private :: serializable_is_same + generic :: operator(==) => serializable_is_same - end type fpm_serializable + !> Test load/write roundtrip + procedure, non_overridable :: test_serialization + + end type serializable_t abstract interface !> Write object to TOML datastructure - subroutine fpm_to_toml(self, table, error) - import fpm_serializable,toml_table,error_t + subroutine to_toml(self, table, error) + import serializable_t,toml_table,error_t implicit none - !> Instance of the dependency tree - class(fpm_serializable), intent(inout) :: self + !> Instance of the serializable object + class(serializable_t), intent(inout) :: self !> Data structure type(toml_table), intent(inout) :: table @@ -58,17 +68,71 @@ subroutine fpm_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - end subroutine fpm_to_toml + end subroutine to_toml + + !> Read dependency tree from TOML data structure + subroutine from_toml(self, table, error) + import serializable_t,toml_table,error_t + implicit none + + !> Instance of the serializable object + class(serializable_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + end subroutine from_toml + + !> Compare two serializable objects + logical function is_equal(this,that) + import serializable_t + class(serializable_t), intent(in) :: this,that + end function is_equal end interface contains + !> Test serialization of a serializable object + subroutine test_serialization(self, error) + class(serializable_t), intent(inout) :: self + type(error_t), allocatable, intent(out) :: error + + integer :: iunit + class(serializable_t), allocatable :: copy + + open(newunit=iunit,form='formatted',status='scratch') + + !> Dump to scratch file + call self%dump(iunit, error) + if (allocated(error)) return + + !> Load from scratch file + rewind(iunit) + allocate(copy,mold=self) + call self%load(iunit,error) + if (allocated(error)) return + close(iunit) + + !> Check same + if (SAME_TYPE_AS(self,copy)) then + if (.not.(self==copy)) then + call fatal_error(error,'serializable object failed TOML write/reread test') + return + end if + end if + deallocate(copy) + + end subroutine test_serialization + !> Write serializable object to a formatted Fortran unit subroutine dump_to_unit(self, unit, error) !> Instance of the dependency tree - class(fpm_serializable), intent(inout) :: self + class(serializable_t), intent(inout) :: self !> Formatted unit integer, intent(in) :: unit !> Error handling @@ -88,7 +152,7 @@ end subroutine dump_to_unit !> Write serializable object to file subroutine dump_to_file(self, file, error) !> Instance of the dependency tree - class(fpm_serializable), intent(inout) :: self + class(serializable_t), intent(inout) :: self !> File name character(len=*), intent(in) :: file !> Error handling @@ -103,6 +167,51 @@ subroutine dump_to_file(self, file, error) end subroutine dump_to_file + !> Read dependency tree from file + subroutine load_from_file(self, file, error) + !> Instance of the dependency tree + class(serializable_t), intent(inout) :: self + !> File name + character(len=*), intent(in) :: file + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: unit + logical :: exist + + inquire (file=file, exist=exist) + if (.not. exist) return + + open (file=file, newunit=unit) + call self%load(unit, error) + close (unit) + end subroutine load_from_file + + !> Read dependency tree from file + subroutine load_from_unit(self, unit, error) + !> Instance of the dependency tree + class(serializable_t), intent(inout) :: self + !> File name + integer, intent(in) :: unit + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_error), allocatable :: parse_error + type(toml_table), allocatable :: table + + call toml_load(table, unit, error=parse_error) + + if (allocated(parse_error)) then + allocate (error) + call move_alloc(parse_error%message, error%message) + return + end if + + call self%load(table, error) + if (allocated(error)) return + + end subroutine load_from_unit + !> Process the configuration file to a TOML data structure subroutine read_package_file(table, manifest, error) From d5ab4526a5b60a6ef6ad426143a5f166e0804fd3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 14:54:00 +0200 Subject: [PATCH 246/799] `git_target_t`: make `serializable_t` --- src/fpm/git.f90 | 110 ++++++++++++++++++++++++++++++++++++++++++++++- src/fpm/toml.f90 | 8 ++-- 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 36b4702f0b..faaaad7eaf 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -2,6 +2,7 @@ module fpm_git use fpm_error, only: error_t, fatal_error use fpm_filesystem, only : get_temp_filename, getline, join_path + use fpm_toml, only: serializable_t, toml_table, get_value implicit none public :: git_target_t @@ -27,6 +28,9 @@ module fpm_git !> Commit hash integer :: revision = 203 + !> Invalid descriptor + integer :: error = -999 + end type enum_descriptor !> Actual enumerator for descriptors @@ -34,7 +38,7 @@ module fpm_git !> Description of an git target - type :: git_target_t + type, extends(serializable_t) :: git_target_t !> Kind of the git target integer :: descriptor = git_descriptor%default @@ -53,6 +57,11 @@ module fpm_git !> Show information on instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => git_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type git_target_t @@ -146,6 +155,30 @@ logical function git_target_eq(this,that) result(is_equal) end function git_target_eq + !> Check that two git targets are equal + logical function git_is_same(this,that) + class(git_target_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + git_is_same = .false. + + select type (other=>that) + type is (git_target_t) + + if (.not.(this%descriptor==other%descriptor)) return + if (.not.(this%url==other%url)) return + if (.not.(this%object==other%object)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + git_is_same = .true. + + end function git_is_same + !> Check that a cached dependency matches a manifest request logical function git_matches_manifest(cached,manifest) @@ -296,5 +329,80 @@ subroutine info(self, unit, verbosity) end subroutine info + !> Dump dependency to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(git_target_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + stop 'dump_to_toml not yet implemented for class git_target_t' + + end subroutine dump_to_toml + + !> Read dependency from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(git_target_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + character(len=:), allocatable :: descriptor_name + + call get_value(table, "descriptor", descriptor_name) + self%descriptor = parse_descriptor(descriptor_name) + + if (self%descriptor==git_descriptor%error) then + call fatal_error(error,"invalid descriptor ID in TOML entry") + return + end if + + !> Target URL of the git repository + call get_value(table, "url", self%url) + + !> Additional descriptor of the git object + call get_value(table,"object", self%object) + + end subroutine load_from_toml + + !> Parse git descriptor identifier from a string + pure integer function parse_descriptor(name) + character(len=*), intent(in) :: name + + select case (name) + case ("default"); parse_descriptor = git_descriptor%default + case ("branch"); parse_descriptor = git_descriptor%branch + case ("tag"); parse_descriptor = git_descriptor%tag + case ("revision"); parse_descriptor = git_descriptor%revision + case default; parse_descriptor = git_descriptor%error + end select + + end function parse_descriptor + + !> Code git descriptor to a string + pure function descriptor_name(descriptor) result(name) + integer, intent(in) :: descriptor + character(len=:), allocatable :: name + + select case (descriptor) + case (git_descriptor%default); name = "default" + case (git_descriptor%branch); name = "branch" + case (git_descriptor%tag); name = "tag" + case (git_descriptor%revision); name = "revision" + case default; name = "ERROR" + end select + + end function descriptor_name end module fpm_git diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index ffd2a58d51..e303766c40 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -118,11 +118,9 @@ subroutine test_serialization(self, error) close(iunit) !> Check same - if (SAME_TYPE_AS(self,copy)) then - if (.not.(self==copy)) then - call fatal_error(error,'serializable object failed TOML write/reread test') - return - end if + if (.not.(self==copy)) then + call fatal_error(error,'serializable object failed TOML write/reread test') + return end if deallocate(copy) From b798e1476bdd5e5cf5068852b9a864721d5a347e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 15:23:25 +0200 Subject: [PATCH 247/799] `git_target_t` serialization test --- src/fpm/git.f90 | 24 ++++++++++++++++++++++-- src/fpm/toml.f90 | 7 ++++--- test/fpm_test/test_toml.f90 | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index faaaad7eaf..5e668cd8f2 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -2,7 +2,7 @@ module fpm_git use fpm_error, only: error_t, fatal_error use fpm_filesystem, only : get_temp_filename, getline, join_path - use fpm_toml, only: serializable_t, toml_table, get_value + use fpm_toml, only: serializable_t, toml_table, get_value, set_value implicit none public :: git_target_t @@ -341,7 +341,24 @@ subroutine dump_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - stop 'dump_to_toml not yet implemented for class git_target_t' + integer :: ierr + + call set_value(table, "descriptor", descriptor_name(self%descriptor)) + if (allocated(self%url)) then + call set_value(table, "url", self%url, ierr) + if (ierr/=0) then + call fatal_error(error,'git_target_t: cannot set url in TOML table') + return + end if + endif + + if (allocated(self%object)) then + call set_value(table, "object", self%object, ierr) + if (ierr/=0) then + call fatal_error(error,'git_target_t: cannot set object in TOML table') + return + end if + endif end subroutine dump_to_toml @@ -374,6 +391,9 @@ subroutine load_from_toml(self, table, error) !> Additional descriptor of the git object call get_value(table,"object", self%object) + call info(self,unit=6,verbosity=10) + + end subroutine load_from_toml !> Parse git descriptor identifier from a string diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index e303766c40..246bfc27a4 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -97,8 +97,9 @@ end function is_equal contains !> Test serialization of a serializable object - subroutine test_serialization(self, error) + subroutine test_serialization(self, message, error) class(serializable_t), intent(inout) :: self + character(len=*), intent(in) :: message type(error_t), allocatable, intent(out) :: error integer :: iunit @@ -113,13 +114,13 @@ subroutine test_serialization(self, error) !> Load from scratch file rewind(iunit) allocate(copy,mold=self) - call self%load(iunit,error) + call copy%load(iunit,error) if (allocated(error)) return close(iunit) !> Check same if (.not.(self==copy)) then - call fatal_error(error,'serializable object failed TOML write/reread test') + call fatal_error(error,'serializable object failed TOML write/reread test: '//trim(message)) return end if deallocate(copy) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 1ffea1d651..8aa09c85da 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -2,6 +2,7 @@ module test_toml use testsuite, only : new_unittest, unittest_t, error_t use fpm_toml + use fpm_git implicit none private @@ -20,7 +21,8 @@ subroutine collect_toml(testsuite) testsuite = [ & & new_unittest("valid-toml", test_valid_toml), & & new_unittest("invalid-toml", test_invalid_toml, should_fail=.true.), & - & new_unittest("missing-file", test_missing_file, should_fail=.true.)] + & new_unittest("missing-file", test_missing_file, should_fail=.true.), & + & new_unittest("serialize-git-target", git_target_roundtrip_1)] end subroutine collect_toml @@ -103,5 +105,36 @@ subroutine test_missing_file(error) end subroutine test_missing_file + !> Test git_target_t serialization + subroutine git_target_roundtrip_1(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), allocatable :: table + + type(git_target_t) :: git + + ! Revision type + git = git_target_revision(url="https://github.com/urbanjost/M_CLI2.git", & + sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") + call git%test_serialization("git_target_roundtrip_1",error) + if (allocated(error)) return + + ! Branch type + git = git_target_branch(url="https://github.com/urbanjost/M_CLI2.git", & + branch="main") + call git%test_serialization("git_target_roundtrip_1",error) + if (allocated(error)) return + + ! Branch type + git = git_target_tag(url="https://github.com/urbanjost/M_CLI2.git", & + tag="1.0.0") + call git%test_serialization("git_target_roundtrip_1",error) + if (allocated(error)) return + + end subroutine git_target_roundtrip_1 + + end module test_toml From 85af013dfc81d0e76d204cdef91c9022dd2ba657 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 16:15:36 +0200 Subject: [PATCH 248/799] Dependency update: add CI tests --- ci/run_tests.sh | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index eb360485f6..c8247eb660 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -203,7 +203,26 @@ popd # test dependency priority pushd dependency_priority -"$fpm" run + +# first build should run OK +EXIT_CODE=0 +"$fpm" run || EXIT_CODE=$? +test $EXIT_CODE -eq 0 + +# Build again, should update nothing +"$fpm" build --verbose > build.log +if [[ -n "$(grep Update build.log)" ]]; then + echo "Some dependencies were updated that should not be"; + exit 1; +fi + +# Request update, should update oth +"$fpm" update --clean --verbose > update.log +if [[ -z "$(grep Update update.log)" ]]; then + echo "No updated dependencies after 'fpm update --clean'"; + exit 1; +fi + popd # Cleanup From 3ab251e55bd3a05071ab0e1c8e71e43a2c8a3b54 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 16:34:14 +0200 Subject: [PATCH 249/799] more checks to deploy to CI --- src/fpm/dependency.f90 | 32 +++++++++++++++++++++++++------- src/fpm/git.f90 | 15 +++++++++++++-- src/fpm/manifest/dependency.f90 | 15 ++++++++++++--- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 08365c361d..dbb03d903b 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -310,7 +310,7 @@ subroutine add_project(self, package, error) ! After resolving all dependencies, check if we have cached ones to avoid updates if (allocated(self%cache)) then - call new_dependency_tree(cached, cache=self%cache) + call new_dependency_tree(cached, verbosity=2,cache=self%cache) call cached%load(self%cache, error) if (allocated(error)) return @@ -441,7 +441,7 @@ subroutine add_dependency_node(self, dependency, error) ! the same dependency from a lower branch of the dependency tree, the existing one from ! the manifest has priority if (dependency%cached) then - if (dependency_has_changed(dependency, self%dep(id))) then + if (dependency_has_changed(dependency, self%dep(id), self%verbosity, self%unit)) then if (self%verbosity>0) write (self%unit, out_fmt) "Dependency change detected:", dependency%name self%dep(id)%update = .true. else @@ -1182,26 +1182,44 @@ pure subroutine resize_dependency_node(var, n) end subroutine resize_dependency_node !> Check if a dependency node has changed - logical function dependency_has_changed(cached, manifest) result(has_changed) + logical function dependency_has_changed(cached, manifest, verbosity, iunit) result(has_changed) !> Two instances of the same dependency to be compared type(dependency_node_t), intent(in) :: cached, manifest + !> Log verbosity + integer, intent(in) :: verbosity, iunit + has_changed = .true. !> All the following entities must be equal for the dependency to not have changed - if (manifest_has_changed(cached=cached, manifest=manifest)) return + if (manifest_has_changed(cached=cached, manifest=manifest, verbosity=verbosity, iunit=iunit)) return !> For now, only perform the following checks if both are available. A dependency in cache.toml !> will always have this metadata; a dependency from fpm.toml which has not been fetched yet !> may not have it if (allocated(cached%version) .and. allocated(manifest%version)) then - if (cached%version /= manifest%version) return + if (cached%version /= manifest%version) then + if (verbosity>1) write(iunit,out_fmt) "VERSION has changed: "//cached%version%s()//" vs. "//manifest%version%s() + return + endif + else + if (verbosity>1) write(iunit,out_fmt) "VERSION has changed presence " end if if (allocated(cached%revision) .and. allocated(manifest%revision)) then - if (cached%revision /= manifest%revision) return + if (cached%revision /= manifest%revision) then + if (verbosity>1) write(iunit,out_fmt) "REVISION has changed: "//cached%revision//" vs. "//manifest%revision + return + endif + else + if (verbosity>1) write(iunit,out_fmt) "REVISION has changed presence " end if if (allocated(cached%proj_dir) .and. allocated(manifest%proj_dir)) then - if (cached%proj_dir /= manifest%proj_dir) return + if (cached%proj_dir /= manifest%proj_dir) then + if (verbosity>1) write(iunit,out_fmt) "PROJECT DIR has changed: "//cached%proj_dir//" vs. "//manifest%proj_dir + return + endif + else + if (verbosity>1) write(iunit,out_fmt) "PROJECT DIR has changed presence " end if !> All checks passed: the two dependencies have no differences diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 36b4702f0b..bd0af2b443 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -60,6 +60,9 @@ module fpm_git module procedure git_target_eq end interface + !> Common output format for writing to the command line + character(len=*), parameter :: out_fmt = '("#", *(1x, g0))' + contains @@ -147,19 +150,27 @@ logical function git_target_eq(this,that) result(is_equal) end function git_target_eq !> Check that a cached dependency matches a manifest request - logical function git_matches_manifest(cached,manifest) + logical function git_matches_manifest(cached,manifest,verbosity,iunit) !> Two input git targets type(git_target_t), intent(in) :: cached,manifest + integer, intent(in) :: verbosity,iunit + git_matches_manifest = cached%url == manifest%url - if (.not.git_matches_manifest) return + if (.not.git_matches_manifest) then + if (verbosity>1) write(iunit,out_fmt) "GIT URL has changed: ",cached%url," vs. ", manifest%url + return + endif !> The manifest dependency only contains partial information (what's requested), !> while the cached dependency always stores a commit hash because it's built !> after the repo is available (saved as git_descriptor%revision==revision). !> So, comparing against the descriptor is not reliable git_matches_manifest = cached%object == manifest%object + if (.not.git_matches_manifest) then + if (verbosity>1) write(iunit,out_fmt) "GIT OBJECT has changed: ",cached%object," vs. ", manifest%object + end if end function git_matches_manifest diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 432137f655..1ca53bc9cf 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -63,6 +63,9 @@ module fpm_manifest_dependency end type dependency_config_t + !> Common output format for writing to the command line + character(len=*), parameter :: out_fmt = '("#", *(1x, g0))' + contains !> Construct a new dependency configuration from a TOML data structure @@ -274,17 +277,23 @@ subroutine info(self, unit, verbosity) end subroutine info !> Check if two dependency configurations are different - logical function manifest_has_changed(cached, manifest) result(has_changed) + logical function manifest_has_changed(cached, manifest, verbosity, iunit) result(has_changed) !> Two instances of the dependency configuration class(dependency_config_t), intent(in) :: cached, manifest + !> Log verbosity + integer, intent(in) :: verbosity, iunit + has_changed = .true. !> Perform all checks - if (allocated(cached%git).neqv.allocated(manifest%git)) return + if (allocated(cached%git).neqv.allocated(manifest%git)) then + if (verbosity>1) write(iunit,out_fmt) "GIT presence has changed. " + return + endif if (allocated(cached%git)) then - if (.not.git_matches_manifest(cached%git,manifest%git)) return + if (.not.git_matches_manifest(cached%git,manifest%git,verbosity,iunit)) return end if !> All checks passed! The two instances are equal From d148e69f3e39ed89593e3934e227a8da203d5a10 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 16:38:25 +0200 Subject: [PATCH 250/799] temporary: add more CI tests --- ci/run_tests.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index c8247eb660..a00e779014 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -204,11 +204,16 @@ popd # test dependency priority pushd dependency_priority +# temporaray: add more output +"$fpm" --version + # first build should run OK EXIT_CODE=0 "$fpm" run || EXIT_CODE=$? test $EXIT_CODE -eq 0 +"$fpm" build --verbose + # Build again, should update nothing "$fpm" build --verbose > build.log if [[ -n "$(grep Update build.log)" ]]; then From 0da28ccfe1ead7a3d4f74307386c894a0792c8ec Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 16:43:56 +0200 Subject: [PATCH 251/799] do not flag new dependency for update --- src/fpm/dependency.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index dbb03d903b..fa408963ed 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -454,6 +454,7 @@ subroutine add_dependency_node(self, dependency, error) ! New dependency: add from scratch self%ndep = self%ndep + 1 self%dep(self%ndep) = dependency + self%dep(self%ndep)%update = .false. end if end subroutine add_dependency_node From 0e231b508617fbf302df15d7a3e74f70c684154c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 16:46:57 +0200 Subject: [PATCH 252/799] display build output --- ci/run_tests.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index a00e779014..51b1016a4b 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -217,6 +217,9 @@ test $EXIT_CODE -eq 0 # Build again, should update nothing "$fpm" build --verbose > build.log if [[ -n "$(grep Update build.log)" ]]; then + + cat build.log; + echo "Some dependencies were updated that should not be"; exit 1; fi @@ -224,6 +227,9 @@ fi # Request update, should update oth "$fpm" update --clean --verbose > update.log if [[ -z "$(grep Update update.log)" ]]; then + + cat update.log; + echo "No updated dependencies after 'fpm update --clean'"; exit 1; fi From abcd478615f102369e28e36fd37c3663d145c44d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 16:52:26 +0200 Subject: [PATCH 253/799] temporary: add prints --- src/fpm/dependency.f90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index fa408963ed..79c77b40a6 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -960,6 +960,9 @@ subroutine register(self, package, root, fetch, revision, error) self%update = update self%done = .true. + if (self%update) print *, 'register: set '//self%name//' for update, has revision? ',present(revision),' fetch? ',fetch + if (self%update .and. present(revision)) print *, ' git object=',self%git%object,' revision=',revision + end subroutine register !> Read dependency tree from file From a1ea7c584e9eff95d95dc9c9665d7fc6321d630d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 17:05:22 +0200 Subject: [PATCH 254/799] check if `fetch` is uninitialized --- src/fpm/dependency.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 79c77b40a6..885b91075e 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -602,6 +602,8 @@ subroutine resolve_dependency(self, dependency, global_settings, root, error) call get_package_data(package, manifest, error) if (allocated(error)) return + print *, 'dependency',dependency%name,': fetch=',fetch,' allocated(git)=',allocated(dependency%git),' proj_dir=',proj_dir,' fetch=',fetch + call dependency%register(package, proj_dir, fetch, revision, error) if (allocated(error)) return @@ -960,8 +962,8 @@ subroutine register(self, package, root, fetch, revision, error) self%update = update self%done = .true. - if (self%update) print *, 'register: set '//self%name//' for update, has revision? ',present(revision),' fetch? ',fetch - if (self%update .and. present(revision)) print *, ' git object=',self%git%object,' revision=',revision + print *, 'register: set '//self%name//' for update, has revision? ',present(revision),' fetch? ',fetch,' set update? ',self%update + if (present(revision)) print *, ' git object=',self%git%object,' revision=',revision end subroutine register From 9d2cac290d1031ab58a0199b1211a06334d15182 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 17:29:26 +0200 Subject: [PATCH 255/799] restore deterministic update policy --- src/fpm/cmd/update.f90 | 7 +++++++ src/fpm/dependency.f90 | 10 ++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index 41e1dc3f14..e1bcb7326c 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -40,6 +40,13 @@ subroutine cmd_update(settings) call deps%add(package, error) call handle_error(error) + ! Force-update all dependencies if `--clean` + if (settings%clean) then + do ii = 1, deps%ndep + deps%dep(ii)%update = .true. + end do + end if + if (settings%fetch_only) return if (size(settings%name) == 0) then diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 885b91075e..c8c29a545c 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -454,7 +454,6 @@ subroutine add_dependency_node(self, dependency, error) ! New dependency: add from scratch self%ndep = self%ndep + 1 self%dep(self%ndep) = dependency - self%dep(self%ndep)%update = .false. end if end subroutine add_dependency_node @@ -577,6 +576,7 @@ subroutine resolve_dependency(self, dependency, global_settings, root, error) if (dependency%done) return + fetch = .false. if (allocated(dependency%proj_dir)) then proj_dir = dependency%proj_dir else if (allocated(dependency%path)) then @@ -604,6 +604,7 @@ subroutine resolve_dependency(self, dependency, global_settings, root, error) print *, 'dependency',dependency%name,': fetch=',fetch,' allocated(git)=',allocated(dependency%git),' proj_dir=',proj_dir,' fetch=',fetch + call dependency%register(package, proj_dir, fetch, revision, error) if (allocated(error)) return @@ -950,16 +951,13 @@ subroutine register(self, package, root, fetch, revision, error) if (allocated(self%git) .and. present(revision)) then self%revision = revision if (.not. fetch) then + ! Change in revision ID was checked already. Only update if git information is missing ! git object is HEAD always allows an update update = .not. allocated(self%git%object) - if (.not. update) then - ! allow update in case the revision does not match the requested object - update = revision /= self%git%object - end if end if end if - self%update = update + if (update) self%update = update self%done = .true. print *, 'register: set '//self%name//' for update, has revision? ',present(revision),' fetch? ',fetch,' set update? ',self%update From a3f70b147b7ea875881e7fdf14fa69d0313c5519 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 17:30:59 +0200 Subject: [PATCH 256/799] shorten line --- src/fpm/dependency.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index c8c29a545c..e5bc8b01ad 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -602,7 +602,8 @@ subroutine resolve_dependency(self, dependency, global_settings, root, error) call get_package_data(package, manifest, error) if (allocated(error)) return - print *, 'dependency',dependency%name,': fetch=',fetch,' allocated(git)=',allocated(dependency%git),' proj_dir=',proj_dir,' fetch=',fetch + print *, 'dependency',dependency%name,': fetch=',fetch,' allocated(git)=',allocated(dependency%git) + print *, ' proj_dir=',proj_dir,' fetch=',fetch call dependency%register(package, proj_dir, fetch, revision, error) From d9db9db8f9a71b33e7d5a76bf0bdf5682cc877ba Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 17:33:30 +0200 Subject: [PATCH 257/799] shorten line --- src/fpm/dependency.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index e5bc8b01ad..8760306605 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -961,7 +961,8 @@ subroutine register(self, package, root, fetch, revision, error) if (update) self%update = update self%done = .true. - print *, 'register: set '//self%name//' for update, has revision? ',present(revision),' fetch? ',fetch,' set update? ',self%update + print *, 'register: set '//self%name//' for update, has revision? ',present(revision),& + ' fetch? ',fetch,' set update? ',self%update if (present(revision)) print *, ' git object=',self%git%object,' revision=',revision end subroutine register From 3a31380b8649b16a3c7af382b11b4514ffea13ba Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 17:43:06 +0200 Subject: [PATCH 258/799] remove testing bloatware --- ci/run_tests.sh | 8 +------- src/fpm/dependency.f90 | 10 +--------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 51b1016a4b..599c2247a0 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -217,19 +217,13 @@ test $EXIT_CODE -eq 0 # Build again, should update nothing "$fpm" build --verbose > build.log if [[ -n "$(grep Update build.log)" ]]; then - - cat build.log; - echo "Some dependencies were updated that should not be"; exit 1; fi -# Request update, should update oth +# Request update --clean, should update all dependencies "$fpm" update --clean --verbose > update.log if [[ -z "$(grep Update update.log)" ]]; then - - cat update.log; - echo "No updated dependencies after 'fpm update --clean'"; exit 1; fi diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 8760306605..aa268506be 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -310,7 +310,7 @@ subroutine add_project(self, package, error) ! After resolving all dependencies, check if we have cached ones to avoid updates if (allocated(self%cache)) then - call new_dependency_tree(cached, verbosity=2,cache=self%cache) + call new_dependency_tree(cached, verbosity=self%verbosity,cache=self%cache) call cached%load(self%cache, error) if (allocated(error)) return @@ -602,10 +602,6 @@ subroutine resolve_dependency(self, dependency, global_settings, root, error) call get_package_data(package, manifest, error) if (allocated(error)) return - print *, 'dependency',dependency%name,': fetch=',fetch,' allocated(git)=',allocated(dependency%git) - print *, ' proj_dir=',proj_dir,' fetch=',fetch - - call dependency%register(package, proj_dir, fetch, revision, error) if (allocated(error)) return @@ -961,10 +957,6 @@ subroutine register(self, package, root, fetch, revision, error) if (update) self%update = update self%done = .true. - print *, 'register: set '//self%name//' for update, has revision? ',present(revision),& - ' fetch? ',fetch,' set update? ',self%update - if (present(revision)) print *, ' git object=',self%git%object,' revision=',revision - end subroutine register !> Read dependency tree from file From 65149b5b184950f722565bf938f091e9aa34cd03 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 17:59:21 +0200 Subject: [PATCH 259/799] fix update of git dependency with url only --- src/fpm/dependency.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index aa268506be..d89b6eb836 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -454,6 +454,7 @@ subroutine add_dependency_node(self, dependency, error) ! New dependency: add from scratch self%ndep = self%ndep + 1 self%dep(self%ndep) = dependency + self%dep(self%ndep)%update = .false. end if end subroutine add_dependency_node @@ -948,9 +949,8 @@ subroutine register(self, package, root, fetch, revision, error) if (allocated(self%git) .and. present(revision)) then self%revision = revision if (.not. fetch) then - ! Change in revision ID was checked already. Only update if git information is missing - ! git object is HEAD always allows an update - update = .not. allocated(self%git%object) + ! Change in revision ID was checked already. Only update if ALL git information is missing + update = .not. allocated(self%git%url) end if end if From cd8dc7dab458314005143216807d74e5ce748df5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 18:14:24 +0200 Subject: [PATCH 260/799] remove temporary bloatware --- ci/run_tests.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 599c2247a0..8e6162d81f 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -204,9 +204,6 @@ popd # test dependency priority pushd dependency_priority -# temporaray: add more output -"$fpm" --version - # first build should run OK EXIT_CODE=0 "$fpm" run || EXIT_CODE=$? From d2c94256a95c00321116ab1d9cf351a43ce25c9a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 11 Apr 2023 18:15:24 +0200 Subject: [PATCH 261/799] restore 0.8.0 version --- fpm.toml | 2 +- src/fpm_command_line.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index de6c1b073f..e59941e417 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.8.1" +version = "0.8.0" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index fd9fd90ea9..4a434deb4b 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -235,7 +235,7 @@ subroutine get_command_line_settings(cmd_settings) end select unix = os_is_unix(os) version_text = [character(len=80) :: & - & 'Version: 0.8.1, alpha', & + & 'Version: 0.8.0, alpha', & & 'Program: fpm(1)', & & 'Description: A Fortran package manager and build system', & & 'Home Page: https://github.com/fortran-lang/fpm', & From ee42b02542228aea69e3667b26c6a78a3cd9659c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 09:13:47 +0200 Subject: [PATCH 262/799] bump version to 0.8.1 --- fpm.toml | 2 +- src/fpm_command_line.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index e59941e417..de6c1b073f 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.8.0" +version = "0.8.1" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 4a434deb4b..fd9fd90ea9 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -235,7 +235,7 @@ subroutine get_command_line_settings(cmd_settings) end select unix = os_is_unix(os) version_text = [character(len=80) :: & - & 'Version: 0.8.0, alpha', & + & 'Version: 0.8.1, alpha', & & 'Program: fpm(1)', & & 'Description: A Fortran package manager and build system', & & 'Home Page: https://github.com/fortran-lang/fpm', & From 0fbeb0a79371e4ca6156e5b8b5b427254badc778 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 10:01:58 +0200 Subject: [PATCH 263/799] `git_target_t` tests --- test/fpm_test/test_toml.f90 | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 8aa09c85da..dcbd4abbfb 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -22,7 +22,7 @@ subroutine collect_toml(testsuite) & new_unittest("valid-toml", test_valid_toml), & & new_unittest("invalid-toml", test_invalid_toml, should_fail=.true.), & & new_unittest("missing-file", test_missing_file, should_fail=.true.), & - & new_unittest("serialize-git-target", git_target_roundtrip_1)] + & new_unittest("serialize-git-target", git_target_roundtrip)] end subroutine collect_toml @@ -106,7 +106,7 @@ subroutine test_missing_file(error) end subroutine test_missing_file !> Test git_target_t serialization - subroutine git_target_roundtrip_1(error) + subroutine git_target_roundtrip(error) !> Error handling type(error_t), allocatable, intent(out) :: error @@ -118,22 +118,32 @@ subroutine git_target_roundtrip_1(error) ! Revision type git = git_target_revision(url="https://github.com/urbanjost/M_CLI2.git", & sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") - call git%test_serialization("git_target_roundtrip_1",error) + call git%test_serialization("revision git type",error) if (allocated(error)) return ! Branch type git = git_target_branch(url="https://github.com/urbanjost/M_CLI2.git", & branch="main") - call git%test_serialization("git_target_roundtrip_1",error) + call git%test_serialization("branch git type",error) if (allocated(error)) return - ! Branch type + ! Tag type git = git_target_tag(url="https://github.com/urbanjost/M_CLI2.git", & tag="1.0.0") - call git%test_serialization("git_target_roundtrip_1",error) + call git%test_serialization("target git type",error) + if (allocated(error)) return + + ! Incomplete type + if (allocated(git%object)) deallocate(git%object) + call git%test_serialization("incomplete git type 1/2",error) + if (allocated(error)) return + + ! Incomplete type + if (allocated(git%url)) deallocate(git%url) + call git%test_serialization("incomplete git type 2/2",error) if (allocated(error)) return - end subroutine git_target_roundtrip_1 + end subroutine git_target_roundtrip From 4bca99b8407ad9335605eaba6fbd878f64c92089 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 10:51:36 +0200 Subject: [PATCH 264/799] `dependency_config_t`: implement `serializable_t` --- src/fpm/git.f90 | 9 +- src/fpm/manifest/dependency.f90 | 170 +++++++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 10 deletions(-) diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 61e8719bd3..64cca94188 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -2,7 +2,7 @@ module fpm_git use fpm_error, only: error_t, fatal_error use fpm_filesystem, only : get_temp_filename, getline, join_path - use fpm_toml, only: serializable_t, toml_table, get_value, set_value + use fpm_toml, only: serializable_t, toml_table, get_value, set_value, toml_stat implicit none public :: git_target_t @@ -357,7 +357,7 @@ subroutine dump_to_toml(self, table, error) call set_value(table, "descriptor", descriptor_name(self%descriptor)) if (allocated(self%url)) then call set_value(table, "url", self%url, ierr) - if (ierr/=0) then + if (ierr/=toml_stat%success) then call fatal_error(error,'git_target_t: cannot set url in TOML table') return end if @@ -365,7 +365,7 @@ subroutine dump_to_toml(self, table, error) if (allocated(self%object)) then call set_value(table, "object", self%object, ierr) - if (ierr/=0) then + if (ierr/=toml_stat%success) then call fatal_error(error,'git_target_t: cannot set object in TOML table') return end if @@ -402,9 +402,6 @@ subroutine load_from_toml(self, table, error) !> Additional descriptor of the git object call get_value(table,"object", self%object) - call info(self,unit=6,verbosity=10) - - end subroutine load_from_toml !> Parse git descriptor identifier from a string diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 1ca53bc9cf..1a14b2ef16 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -23,20 +23,22 @@ !> Resolving a dependency will result in obtaining a new package configuration !> data for the respective project. module fpm_manifest_dependency - use fpm_error, only: error_t, syntax_error + use fpm_error, only: error_t, syntax_error, fatal_error use fpm_git, only: git_target_t, git_target_tag, git_target_branch, & & git_target_revision, git_target_default, operator(==), git_matches_manifest - use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys + use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys, serializable_t, add_table, & + & set_value use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS use fpm_versioning, only: version_t, new_version implicit none private - public :: dependency_config_t, new_dependency, new_dependencies, manifest_has_changed + public :: dependency_config_t, new_dependency, new_dependencies, manifest_has_changed, & + & dependency_destroy !> Configuration meta data for a dependency - type :: dependency_config_t + type, extends(serializable_t) :: dependency_config_t !> Name of the dependency character(len=:), allocatable :: name @@ -61,6 +63,11 @@ module fpm_manifest_dependency !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => dependency_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type dependency_config_t !> Common output format for writing to the command line @@ -301,5 +308,160 @@ logical function manifest_has_changed(cached, manifest, verbosity, iunit) result end function manifest_has_changed + !> Clean memory + elemental subroutine dependency_destroy(self) + class(dependency_config_t), intent(inout) :: self + + if (allocated(self%name)) deallocate(self%name) + if (allocated(self%path)) deallocate(self%path) + if (allocated(self%namespace)) deallocate(self%namespace) + if (allocated(self%requested_version)) deallocate(self%requested_version) + if (allocated(self%git)) deallocate(self%git) + + end subroutine dependency_destroy + + !> Check that two dependency configs are equal + logical function dependency_is_same(this,that) + class(dependency_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + dependency_is_same = .false. + + select type (other=>that) + type is (dependency_config_t) + + if (.not.(this%name==other%name)) return + if (.not.(this%path==other%path)) return + if (.not.(this%namespace==other%namespace)) return + if (.not.(allocated(this%requested_version).eqv.allocated(other%requested_version))) return + if (allocated(this%requested_version)) then + if (.not.(this%requested_version==other%requested_version)) return + endif + + if (.not.(allocated(this%git).eqv.allocated(other%git))) return + if (allocated(this%git)) then + if (.not.(this%git==other%git)) return + endif + + class default + ! Not the same type + return + end select + + !> All checks passed! + dependency_is_same = .true. + + end function dependency_is_same + + !> Dump dependency to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(dependency_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(toml_table), pointer :: ptr + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + if (allocated(self%name)) then + call set_value(table, "name", self%name, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_config_t: cannot set name in TOML table') + return + end if + endif + + if (allocated(self%path)) then + call set_value(table, "path", self%path, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_config_t: cannot set path in TOML table') + return + end if + endif + + if (allocated(self%namespace)) then + call set_value(table, "namespace", self%namespace, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_config_t: cannot set namespace in TOML table') + return + end if + endif + + if (allocated(self%requested_version)) then + call set_value(table, "requested_version", self%requested_version%s(), ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_config_t: cannot set requested_version in TOML table') + return + end if + endif + + + if (allocated(self%git)) then + call add_table(table, "git", ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_config_t: cannot set git table in TOML table') + return + end if + + call self%git%dump_to_toml(ptr, error) + if (allocated(error)) return + endif + + end subroutine dump_to_toml + + !> Read dependency from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(dependency_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + type(toml_key), allocatable :: list(:) + type(toml_table), pointer :: ptr + character(len=:), allocatable :: requested_version + integer :: ierr,ii + + call dependency_destroy(self) + + call get_value(table, "name", self%name) + call get_value(table, "path", self%path) + call get_value(table, "namespace", self%namespace) + call get_value(table, "requested_version", requested_version) + if (allocated(requested_version)) then + allocate(self%requested_version) + call new_version(self%requested_version, requested_version, error) + if (allocated(error)) then + error%message = 'dependency_config_t: version error from TOML table - '//error%message + return + endif + end if + + call table%get_keys(list) + add_git: do ii = 1, size(list) + if (list(ii)%key=="git") then + call get_value(table, list(ii)%key, ptr, stat=ierr) + if (ierr /= toml_stat%success) then + call fatal_error(error,'dependency_config_t: cannot retrieve git from TOML table') + exit + endif + allocate(self%git) + call self%git%load_from_toml(ptr, error) + if (allocated(error)) return + exit add_git + end if + end do add_git + + end subroutine load_from_toml end module fpm_manifest_dependency From 65833ff3217d6d3d0bb9c914bf8fdb8de064d912 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 10:51:47 +0200 Subject: [PATCH 265/799] test `dependency_config_t` serialization --- test/fpm_test/test_toml.f90 | 58 ++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index dcbd4abbfb..723777e30c 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -3,6 +3,8 @@ module test_toml use testsuite, only : new_unittest, unittest_t, error_t use fpm_toml use fpm_git + use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy + use fpm_versioning, only: new_version implicit none private @@ -22,7 +24,8 @@ subroutine collect_toml(testsuite) & new_unittest("valid-toml", test_valid_toml), & & new_unittest("invalid-toml", test_invalid_toml, should_fail=.true.), & & new_unittest("missing-file", test_missing_file, should_fail=.true.), & - & new_unittest("serialize-git-target", git_target_roundtrip)] + & new_unittest("serialize-git-target", git_target_roundtrip), & + & new_unittest("serialize-dependency-config", dependency_config_roundtrip)] end subroutine collect_toml @@ -146,5 +149,58 @@ subroutine git_target_roundtrip(error) end subroutine git_target_roundtrip + !> Test git_target_t serialization + subroutine dependency_config_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), allocatable :: table + + type(dependency_config_t) :: dep + + call dependency_destroy(dep) + + dep%name = "M_CLI2" + dep%path = "~/./some/dummy/path" + dep%namespace = "urbanjost" + allocate(dep%requested_version) + call new_version(dep%requested_version, "3.2.0",error); if (allocated(error)) return + + allocate(dep%git) + dep%git = git_target_revision(url="https://github.com/urbanjost/M_CLI2.git", & + sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") + + ! Test full object + call dep%test_serialization("full object",error) + if (allocated(error)) return + + ! Remove namespace + deallocate(dep%namespace) + call dep%test_serialization("no namespace",error) + if (allocated(error)) return + + ! Remove git + deallocate(dep%git) + call dep%test_serialization("no git",error) + if (allocated(error)) return + + ! Remove version + deallocate(dep%requested_version) + call dep%test_serialization("no version",error) + if (allocated(error)) return + + ! Remove name + deallocate(dep%name) + call dep%test_serialization("no name",error) + if (allocated(error)) return + + ! Remove path + deallocate(dep%path) + call dep%test_serialization("no path",error) + if (allocated(error)) return + + end subroutine dependency_config_roundtrip + end module test_toml From 0a1e97efce468e194ff37df3e8513329fcbe559a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 11:20:20 +0200 Subject: [PATCH 266/799] `dependency_node_t`: implement `serializable_t` --- src/fpm/dependency.f90 | 172 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 4 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index d89b6eb836..5947ea9497 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -58,10 +58,12 @@ module fpm_dependency use, intrinsic :: iso_fortran_env, only: output_unit use fpm_environment, only: get_os_type, OS_WINDOWS, os_is_unix use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, os_delete_dir - use fpm_git, only: git_target_revision, git_target_default, git_revision, operator(==) + use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, & + basename, os_delete_dir + use fpm_git, only: git_target_revision, git_target_default, git_revision, operator(==), & + serializable_t use fpm_manifest, only: package_config_t, dependency_config_t, get_package_data - use fpm_manifest_dependency, only: manifest_has_changed + use fpm_manifest_dependency, only: manifest_has_changed, dependency_destroy use fpm_strings, only: string_t, operator(.in.) use fpm_toml, only: toml_table, toml_key, toml_error, toml_serialize, & get_value, set_value, add_table, toml_load, toml_stat @@ -74,7 +76,7 @@ module fpm_dependency private public :: dependency_tree_t, new_dependency_tree, dependency_node_t, new_dependency_node, resize, & - & check_and_read_pkg_data + & check_and_read_pkg_data, destroy_dependency_node !> Overloaded reallocation interface interface resize @@ -103,6 +105,13 @@ module fpm_dependency procedure, private :: get_from_local_registry !> Print information on this instance procedure :: info + + !> Serialization interface + procedure :: serializable_is_same => dependency_node_is_same + procedure :: dump_to_toml => node_dump_to_toml + procedure :: load_from_toml => node_load_from_toml + + end type dependency_node_t !> Respresentation of a projects dependencies @@ -1225,4 +1234,159 @@ logical function dependency_has_changed(cached, manifest, verbosity, iunit) resu end function dependency_has_changed + !> Check that two dependency nodes are equal + logical function dependency_node_is_same(this,that) + class(dependency_node_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + dependency_node_is_same = .false. + + select type (other=>that) + type is (dependency_node_t) + + ! Base class must match + if (.not.(this%dependency_config_t==other%dependency_config_t)) return + + ! Extension must match + if (.not.(this%done .eqv.other%done)) return + if (.not.(this%update.eqv.other%update)) return + if (.not.(this%cached.eqv.other%cached)) return + if (.not.(this%proj_dir==other%proj_dir)) return + if (.not.(this%revision==other%revision)) return + + if (.not.(allocated(this%version).eqv.allocated(other%version))) return + if (allocated(this%version)) then + if (.not.(this%version==other%version)) return + endif + + class default + ! Not the same type + return + end select + + !> All checks passed! + dependency_node_is_same = .true. + + end function dependency_node_is_same + + !> Dump dependency to toml table + subroutine node_dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(dependency_node_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(toml_table), pointer :: ptr + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + ! Dump parent class + call self%dependency_config_t%dump_to_toml(table, error) + if (allocated(error)) return + + if (allocated(self%version)) then + call set_value(table, "version", self%version%s(), ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot set version in TOML table') + return + end if + endif + + if (allocated(self%proj_dir)) then + call set_value(table, "proj_dir", self%proj_dir, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot set proj_dir in TOML table') + return + end if + endif + + if (allocated(self%revision)) then + call set_value(table, "revision", self%revision, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot set revision in TOML table') + return + end if + endif + + call set_value(table, "done", self%done, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot set done in TOML table') + return + end if + + call set_value(table, "update", self%update, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot set update in TOML table') + return + end if + + call set_value(table, "cached", self%cached, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot set cached in TOML table') + return + end if + + end subroutine node_dump_to_toml + + !> Read dependency from toml table (no checks made at this stage) + subroutine node_load_from_toml(self, table, error) + + !> Instance of the serializable object + class(dependency_node_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + character(len=:), allocatable :: version + + call destroy_dependency_node(self) + + ! Load parent class + call self%dependency_config_t%load_from_toml(table, error) + if (allocated(error)) return + + call get_value(table, "done", self%done) + call get_value(table, "update", self%update) + call get_value(table, "cached", self%cached) + call get_value(table, "proj_dir", self%proj_dir) + call get_value(table, "revision", self%revision) + + call get_value(table, "version", version) + if (allocated(version)) then + allocate(self%version) + call new_version(self%version, version, error) + if (allocated(error)) then + error%message = 'dependency_node_t: version error from TOML table - '//error%message + return + endif + end if + + end subroutine node_load_from_toml + + !> Destructor + elemental subroutine destroy_dependency_node(self) + + class(dependency_node_t), intent(inout) :: self + + integer :: ierr + + call dependency_destroy(self) + + deallocate(self%version,stat=ierr) + deallocate(self%proj_dir,stat=ierr) + deallocate(self%revision,stat=ierr) + self%done = .false. + self%update = .false. + self%cached = .false. + + end subroutine destroy_dependency_node + end module fpm_dependency From b3cba476c685adb6dd5841c8db13115ef8ff8479 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 11:20:33 +0200 Subject: [PATCH 267/799] test `dependency_node_t` serialization --- test/fpm_test/test_toml.f90 | 81 ++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 723777e30c..2f61c3b1ca 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -3,8 +3,10 @@ module test_toml use testsuite, only : new_unittest, unittest_t, error_t use fpm_toml use fpm_git + use fpm_dependency, only: dependency_node_t, destroy_dependency_node use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version + implicit none private @@ -25,7 +27,8 @@ subroutine collect_toml(testsuite) & new_unittest("invalid-toml", test_invalid_toml, should_fail=.true.), & & new_unittest("missing-file", test_missing_file, should_fail=.true.), & & new_unittest("serialize-git-target", git_target_roundtrip), & - & new_unittest("serialize-dependency-config", dependency_config_roundtrip)] + & new_unittest("serialize-dependency-config", dependency_config_roundtrip), & + & new_unittest("serialize-dependency-node", dependency_node_roundtrip)] end subroutine collect_toml @@ -187,7 +190,7 @@ subroutine dependency_config_roundtrip(error) ! Remove version deallocate(dep%requested_version) - call dep%test_serialization("no version",error) + call dep%test_serialization("no requested_version",error) if (allocated(error)) return ! Remove name @@ -202,5 +205,79 @@ subroutine dependency_config_roundtrip(error) end subroutine dependency_config_roundtrip + !> Test dependency_node_t serialization + subroutine dependency_node_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), allocatable :: table + + type(dependency_node_t) :: dep + + call destroy_dependency_node(dep) + + dep%name = "M_CLI2" + dep%path = "~/./some/dummy/path" + dep%proj_dir = "~/./" + dep%namespace = "urbanjost" + dep%revision = "7264878cdb1baff7323cc48596d829ccfe7751b8" + dep%cached = .true. + dep%done = .false. + dep%update = .true. + allocate(dep%requested_version) + call new_version(dep%requested_version, "3.2.0",error); if (allocated(error)) return + allocate(dep%version) + call new_version(dep%version, "4.53.2",error); if (allocated(error)) return + + allocate(dep%git) + dep%git = git_target_revision(url="https://github.com/urbanjost/M_CLI2.git", & + sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") + + ! Test full object + call dep%test_serialization("full object",error) + if (allocated(error)) return + + ! Remove namespace + deallocate(dep%namespace) + call dep%test_serialization("no namespace",error) + if (allocated(error)) return + + ! Remove git + deallocate(dep%git) + call dep%test_serialization("no git",error) + if (allocated(error)) return + + ! Remove version + deallocate(dep%requested_version) + call dep%test_serialization("no requested_version",error) + if (allocated(error)) return + + ! Remove name + deallocate(dep%name) + call dep%test_serialization("no name",error) + if (allocated(error)) return + + ! Remove path + deallocate(dep%path) + call dep%test_serialization("no path",error) + if (allocated(error)) return + + ! Remove revision + deallocate(dep%revision) + call dep%test_serialization("no revision",error) + if (allocated(error)) return + + ! Remove proj_dir + deallocate(dep%proj_dir) + call dep%test_serialization("no proj_dir",error) + if (allocated(error)) return + + ! Remove version + deallocate(dep%version) + call dep%test_serialization("no version",error) + if (allocated(error)) return + + end subroutine dependency_node_roundtrip end module test_toml From bcffaaa40b455ed7dfe24cb73704a3138c389ade Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 11:27:33 +0200 Subject: [PATCH 268/799] rename `proj_dir` key to `proj-dir` --- src/fpm/dependency.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 5947ea9497..43cfb65575 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -111,7 +111,6 @@ module fpm_dependency procedure :: dump_to_toml => node_dump_to_toml procedure :: load_from_toml => node_load_from_toml - end type dependency_node_t !> Respresentation of a projects dependencies @@ -183,6 +182,7 @@ module fpm_dependency procedure, private :: update_dependency !> Update all dependencies in the tree procedure, private :: update_tree + end type dependency_tree_t !> Common output format for writing to the command line @@ -1297,7 +1297,7 @@ subroutine node_dump_to_toml(self, table, error) endif if (allocated(self%proj_dir)) then - call set_value(table, "proj_dir", self%proj_dir, ierr) + call set_value(table, "proj-dir", self%proj_dir, ierr) if (ierr/=toml_stat%success) then call fatal_error(error,'dependency_node_t: cannot set proj_dir in TOML table') return @@ -1356,7 +1356,7 @@ subroutine node_load_from_toml(self, table, error) call get_value(table, "done", self%done) call get_value(table, "update", self%update) call get_value(table, "cached", self%cached) - call get_value(table, "proj_dir", self%proj_dir) + call get_value(table, "proj-dir", self%proj_dir) call get_value(table, "revision", self%revision) call get_value(table, "version", version) From 5f5b4d94262ce23fac2aef29c64e4226b0afdf65 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 11:33:34 +0200 Subject: [PATCH 269/799] keep former cache interface; rename to `load_cache()`, `dump_cache()` --- src/fpm/dependency.f90 | 52 ++++++++++----------- test/fpm_test/test_package_dependencies.f90 | 18 +++---- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 43cfb65575..7e60a22e08 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -161,21 +161,21 @@ module fpm_dependency !> Depedendncy resolution finished procedure :: finished !> Reading of dependency tree - generic :: load => load_from_file, load_from_unit, load_from_toml + generic :: load_cache => load_cache_from_file, load_cache_from_unit, load_cache_from_toml !> Read dependency tree from file - procedure, private :: load_from_file + procedure, private :: load_cache_from_file !> Read dependency tree from formatted unit - procedure, private :: load_from_unit + procedure, private :: load_cache_from_unit !> Read dependency tree from TOML data structure - procedure, private :: load_from_toml + procedure, private :: load_cache_from_toml !> Writing of dependency tree - generic :: dump => dump_to_file, dump_to_unit, dump_to_toml + generic :: dump_cache => dump_cache_to_file, dump_cache_to_unit, dump_cache_to_toml !> Write dependency tree to file - procedure, private :: dump_to_file + procedure, private :: dump_cache_to_file !> Write dependency tree to formatted unit - procedure, private :: dump_to_unit + procedure, private :: dump_cache_to_unit !> Write dependency tree to TOML data structure - procedure, private :: dump_to_toml + procedure, private :: dump_cache_to_toml !> Update dependency tree generic :: update => update_dependency, update_tree !> Update a list of dependencies @@ -320,7 +320,7 @@ subroutine add_project(self, package, error) ! After resolving all dependencies, check if we have cached ones to avoid updates if (allocated(self%cache)) then call new_dependency_tree(cached, verbosity=self%verbosity,cache=self%cache) - call cached%load(self%cache, error) + call cached%load_cache(self%cache, error) if (allocated(error)) return ! Skip root node @@ -339,7 +339,7 @@ subroutine add_project(self, package, error) if (allocated(error)) return if (allocated(self%cache)) then - call self%dump(self%cache, error) + call self%dump_cache(self%cache, error) if (allocated(error)) return end if @@ -969,7 +969,7 @@ subroutine register(self, package, root, fetch, revision, error) end subroutine register !> Read dependency tree from file - subroutine load_from_file(self, file, error) + subroutine load_cache_from_file(self, file, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> File name @@ -984,12 +984,12 @@ subroutine load_from_file(self, file, error) if (.not. exist) return open (file=file, newunit=unit) - call self%load(unit, error) + call self%load_cache(unit, error) close (unit) - end subroutine load_from_file + end subroutine load_cache_from_file !> Read dependency tree from file - subroutine load_from_unit(self, unit, error) + subroutine load_cache_from_unit(self, unit, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> File name @@ -1008,13 +1008,13 @@ subroutine load_from_unit(self, unit, error) return end if - call self%load(table, error) + call self%load_cache(table, error) if (allocated(error)) return - end subroutine load_from_unit + end subroutine load_cache_from_unit !> Read dependency tree from TOML data structure - subroutine load_from_toml(self, table, error) + subroutine load_cache_from_toml(self, table, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> Data structure @@ -1076,10 +1076,10 @@ subroutine load_from_toml(self, table, error) if (allocated(error)) return self%ndep = size(list) - end subroutine load_from_toml + end subroutine load_cache_from_toml !> Write dependency tree to file - subroutine dump_to_file(self, file, error) + subroutine dump_cache_to_file(self, file, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> File name @@ -1090,14 +1090,14 @@ subroutine dump_to_file(self, file, error) integer :: unit open (file=file, newunit=unit) - call self%dump(unit, error) + call self%dump_cache(unit, error) close (unit) if (allocated(error)) return - end subroutine dump_to_file + end subroutine dump_cache_to_file !> Write dependency tree to file - subroutine dump_to_unit(self, unit, error) + subroutine dump_cache_to_unit(self, unit, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> Formatted unit @@ -1108,14 +1108,14 @@ subroutine dump_to_unit(self, unit, error) type(toml_table) :: table table = toml_table() - call self%dump(table, error) + call self%dump_cache(table, error) write (unit, '(a)') toml_serialize(table) - end subroutine dump_to_unit + end subroutine dump_cache_to_unit !> Write dependency tree to TOML datastructure - subroutine dump_to_toml(self, table, error) + subroutine dump_cache_to_toml(self, table, error) !> Instance of the dependency tree class(dependency_tree_t), intent(inout) :: self !> Data structure @@ -1152,7 +1152,7 @@ subroutine dump_to_toml(self, table, error) end do if (allocated(error)) return - end subroutine dump_to_toml + end subroutine dump_cache_to_toml !> Reallocate a list of dependencies pure subroutine resize_dependency_node(var, n) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 3c5b0ee021..425e124dd4 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -105,13 +105,13 @@ subroutine test_cache_dump_load(error) call new_dependency_node(deps%dep(3), dep, proj_dir=dep%path) open (newunit=unit, status='scratch') - call deps%dump(unit, error) + call deps%dump_cache(unit, error) if (.not. allocated(error)) then rewind (unit) call new_dependency_tree(deps) call resize(deps%dep, 2) - call deps%load(unit, error) + call deps%load_cache(unit, error) close (unit) end if if (allocated(error)) return @@ -152,7 +152,7 @@ subroutine test_cache_load_dump(error) call set_value(ptr, "proj-dir", "fpm-tmp4-dir") call new_dependency_tree(deps) - call deps%load(table, error) + call deps%load_cache(table, error) if (allocated(error)) return if (deps%ndep /= 4) then @@ -163,7 +163,7 @@ subroutine test_cache_load_dump(error) call table%destroy table = toml_table() - call deps%dump(table, error) + call deps%dump_cache(table, error) if (allocated(error)) return call table%get_keys(list) @@ -194,7 +194,7 @@ subroutine test_status(error) call set_value(ptr, "git", "https://github.com/fortran-lang/dep2") call new_dependency_tree(deps) - call deps%load(table, error) + call deps%load_cache(table, error) if (allocated(error)) return if (deps%finished()) then @@ -285,7 +285,7 @@ subroutine test_non_updated_dependencies(error) ! Load into a dependency tree call new_dependency_tree(cached) - call cached%load(cache, error) + call cached%load_cache(cache, error) if (allocated(error)) return ! Mark all dependencies as "cached" do ii=1,cached%ndep @@ -309,7 +309,7 @@ subroutine test_non_updated_dependencies(error) ! Load dependencies from manifest call new_dependency_tree(manifest_deps) - call manifest_deps%load(manifest, error) + call manifest_deps%load_cache(manifest, error) call manifest%destroy() if (allocated(error)) return @@ -369,7 +369,7 @@ subroutine test_update_dependencies(error) ! Load into a dependency tree call new_dependency_tree(cached) - call cached%load(cache, error) + call cached%load_cache(cache, error) if (allocated(error)) return ! Mark all dependencies as "cached" do ii=1,cached%ndep @@ -393,7 +393,7 @@ subroutine test_update_dependencies(error) ! Load dependencies from manifest call new_dependency_tree(manifest_deps) - call manifest_deps%load(manifest, error) + call manifest_deps%load_cache(manifest, error) call manifest%destroy() if (allocated(error)) return From a827bcac2e0bf6884818c3c388e377682504b824 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 12:47:11 +0200 Subject: [PATCH 270/799] `dependency_tree_t`: implement `serializable_t` --- src/fpm/dependency.f90 | 178 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 2 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 7e60a22e08..45c9cc44ce 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -117,7 +117,7 @@ module fpm_dependency !> !> The dependencies are stored in a simple array for now, this can be replaced !> with a binary-search tree or a hash table in the future. - type :: dependency_tree_t + type, extends(serializable_t) :: dependency_tree_t !> Unit for IO integer :: unit = output_unit !> Verbosity of printout @@ -183,6 +183,11 @@ module fpm_dependency !> Update all dependencies in the tree procedure, private :: update_tree + !> Serialization interface + procedure :: serializable_is_same => dependency_tree_is_same + procedure :: dump_to_toml => tree_dump_to_toml + procedure :: load_from_toml => tree_load_from_toml + end type dependency_tree_t !> Common output format for writing to the command line @@ -1279,7 +1284,6 @@ subroutine node_dump_to_toml(self, table, error) type(toml_table), intent(inout) :: table !> Error handling - type(toml_table), pointer :: ptr type(error_t), allocatable, intent(out) :: error integer :: ierr @@ -1389,4 +1393,174 @@ elemental subroutine destroy_dependency_node(self) end subroutine destroy_dependency_node + !> Check that two dependency trees are equal + logical function dependency_tree_is_same(this,that) + class(dependency_tree_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + integer :: ii + + dependency_tree_is_same = .false. + + select type (other=>that) + type is (dependency_tree_t) + + if (.not.(this%unit==other%unit)) return + if (.not.(this%verbosity==other%verbosity)) return + if (.not.(this%dep_dir==other%dep_dir)) return + if (.not.(this%ndep==other%ndep)) return + if (.not.(allocated(this%dep).eqv.allocated(other%dep))) return + if (allocated(this%dep)) then + if (.not.(size(this%dep)==size(other%dep))) return + do ii = 1, size(this%dep) + if (.not.(this%dep(ii)==other%dep(ii))) return + end do + endif + if (.not.(this%cache==other%cache)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + dependency_tree_is_same = .true. + + end function dependency_tree_is_same + + !> Dump dependency to toml table + subroutine tree_dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(dependency_tree_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr, ii + type(toml_table), pointer :: ptr_deps,ptr + character(27) :: unnamed + + call set_value(table, "unit", self%unit, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set unit in TOML table') + return + end if + call set_value(table, "verbosity", self%verbosity, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set verbosity in TOML table') + return + end if + if (allocated(self%dep_dir)) then + call set_value(table, "dep-dir", self%dep_dir, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set dep-dir in TOML table') + return + end if + endif + if (allocated(self%cache)) then + call set_value(table, "cache", self%cache, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set cache in TOML table') + return + end if + endif + call set_value(table, "ndep", self%ndep, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set ndep in TOML table') + return + end if + + if (allocated(self%dep)) then + + ! Create dependency table + call add_table(table, "dependencies", ptr_deps) + if (.not. associated(ptr_deps)) then + call fatal_error(error, "dependency_tree_t cannot create dependency table ") + return + end if + + do ii = 1, size(self%dep) + associate (dep => self%dep(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(dep%name)==0) then + write(unnamed,1) ii + call add_table(ptr_deps, trim(unnamed), ptr) + else + call add_table(ptr_deps, dep%name, ptr) + end if + if (.not. associated(ptr)) then + call fatal_error(error, "dependency_tree_t cannot create entry for dependency "//dep%name) + return + end if + call dep%dump_to_toml(ptr, error) + if (allocated(error)) return + end associate + end do + + endif + + 1 format('UNNAMED_DEPENDENCY_',i0) + + end subroutine tree_dump_to_toml + + !> Read dependency from toml table (no checks made at this stage) + subroutine tree_load_from_toml(self, table, error) + + !> Instance of the serializable object + class(dependency_tree_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + type(toml_key), allocatable :: keys(:),dep_keys(:) + type(toml_table), pointer :: ptr_deps,ptr + integer :: ii, jj, ierr + + call table%get_keys(keys) + + call get_value(table, "unit", self%unit) + call get_value(table, "verbosity", self%verbosity) + call get_value(table, "ndep", self%ndep) + call get_value(table, "dep-dir", self%dep_dir) + call get_value(table, "cache", self%cache) + + find_deps_table: do ii = 1, size(keys) + if (keys(ii)%key=="dependencies") then + + call get_value(table, keys(ii), ptr_deps) + if (.not.associated(ptr_deps)) then + call fatal_error(error,'dependency_tree_t: error retrieving dependency table from TOML table') + return + end if + + !> Read all dependencies + call ptr_deps%get_keys(dep_keys) + call resize(self%dep, size(dep_keys)) + + do jj = 1, size(dep_keys) + + call get_value(ptr_deps, dep_keys(jj), ptr) + call self%dep(jj)%load_from_toml(ptr, error) + if (allocated(error)) return + + end do + + exit find_deps_table + + endif + end do find_deps_table + + end subroutine tree_load_from_toml + + end module fpm_dependency From fd46ac09b0c676f08e76217d910d8c36ca54e7cf Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 12:47:24 +0200 Subject: [PATCH 271/799] test `dependency_tree_t` serialization --- test/fpm_test/test_toml.f90 | 81 +++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 2f61c3b1ca..8d3774dff8 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -3,7 +3,8 @@ module test_toml use testsuite, only : new_unittest, unittest_t, error_t use fpm_toml use fpm_git - use fpm_dependency, only: dependency_node_t, destroy_dependency_node + use fpm_dependency, only: dependency_node_t, destroy_dependency_node, dependency_tree_t, & + & new_dependency_node, new_dependency_tree, resize use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version @@ -28,7 +29,8 @@ subroutine collect_toml(testsuite) & new_unittest("missing-file", test_missing_file, should_fail=.true.), & & new_unittest("serialize-git-target", git_target_roundtrip), & & new_unittest("serialize-dependency-config", dependency_config_roundtrip), & - & new_unittest("serialize-dependency-node", dependency_node_roundtrip)] + & new_unittest("serialize-dependency-node", dependency_node_roundtrip), & + & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip)] end subroutine collect_toml @@ -117,8 +119,6 @@ subroutine git_target_roundtrip(error) !> Error handling type(error_t), allocatable, intent(out) :: error - type(toml_table), allocatable :: table - type(git_target_t) :: git ! Revision type @@ -158,8 +158,6 @@ subroutine dependency_config_roundtrip(error) !> Error handling type(error_t), allocatable, intent(out) :: error - type(toml_table), allocatable :: table - type(dependency_config_t) :: dep call dependency_destroy(dep) @@ -211,8 +209,6 @@ subroutine dependency_node_roundtrip(error) !> Error handling type(error_t), allocatable, intent(out) :: error - type(toml_table), allocatable :: table - type(dependency_node_t) :: dep call destroy_dependency_node(dep) @@ -280,4 +276,73 @@ subroutine dependency_node_roundtrip(error) end subroutine dependency_node_roundtrip + !> Test dependency_tree_t serialization + subroutine dependency_tree_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(dependency_tree_t) :: deps + type(dependency_config_t) :: dep + + integer, parameter :: ALLOCATED_DEPS = 5 + character(36) :: msg + integer :: ii + + ! Generate dummy tree with ndep=3 but 5 allocated dependencies + call new_dependency_tree(deps) + call resize(deps%dep, ALLOCATED_DEPS) + deps%ndep = 3 + dep%name = "dep1" + dep%path = "fpm-tmp1-dir" + call new_dependency_node(deps%dep(1), dep, proj_dir=dep%path) + dep%name = "dep2" + dep%path = "fpm-tmp2-dir" + call new_dependency_node(deps%dep(2), dep, proj_dir=dep%path) + deps%dep(3)%name = "M_CLI2" + deps%dep(3)%path = "~/./some/dummy/path" + deps%dep(3)%proj_dir = "~/./" + deps%dep(3)%namespace = "urbanjost" + deps%dep(3)%revision = "7264878cdb1baff7323cc48596d829ccfe7751b8" + deps%dep(3)%cached = .true. + deps%dep(3)%done = .false. + deps%dep(3)%update = .true. + allocate(deps%dep(3)%requested_version) + call new_version(deps%dep(3)%requested_version, "3.2.0",error); if (allocated(error)) return + allocate(deps%dep(3)%version) + call new_version(deps%dep(3)%version, "4.53.2",error); if (allocated(error)) return + allocate(deps%dep(3)%git) + deps%dep(3)%git = git_target_revision(url="https://github.com/urbanjost/M_CLI2.git", & + sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") + + call deps%test_serialization("full dependency tree", error) + if (allocated(error)) then + print *, error%message + stop 'catastrophic' + end if + + ! Remove dependencies (including all them) + do ii = 1, ALLOCATED_DEPS + write(msg,1) ii + call resize(deps%dep, size(deps%dep) - 1) + call deps%test_serialization(trim(msg), error) + if (allocated(error)) return + end do + + ! deallocate dependencies + deallocate(deps%dep) + call deps%test_serialization("unallocated deps(:)", error) + if (allocated(error)) return + + ! Remove deps dir + deallocate(deps%dep_dir) + call deps%test_serialization("no deps dir", error) + if (allocated(error)) return + + + + 1 format('removed ',i0,' dependencies') + + end subroutine dependency_tree_roundtrip + end module test_toml From 7263519acc7cb7d308d31c454886fd754b24d20f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 13:06:28 +0200 Subject: [PATCH 272/799] strings: write 0D, 1D comparison operators --- src/fpm_strings.f90 | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index f8dc4e6daf..d164f76ad2 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -44,6 +44,7 @@ module fpm_strings public :: string_array_contains, string_cat, len_trim, operator(.in.), fnv_1a public :: replace, resize, str, join, glob public :: notabs +public :: operator(==) !> Module naming public :: is_valid_module_name, is_valid_module_prefix, & @@ -89,6 +90,11 @@ module fpm_strings module procedure f_string, f_string_cptr, f_string_cptr_n end interface f_string +interface operator(==) + module procedure string_is_same + module procedure string_arrays_same +end interface + contains !> test if a CHARACTER string ends with a specified suffix @@ -1219,6 +1225,53 @@ logical function has_valid_standard_prefix(module_name,package_name) result(vali end function has_valid_standard_prefix +!> Check that two string _objects_ are exactly identical +pure logical function string_is_same(this,that) + !> two strings to be compared + type(string_t), intent(in) :: this, that + + integer :: i + + string_is_same = .false. + + if (allocated(this%s).neqv.allocated(that%s)) return + if (allocated(this%s)) then + if (.not.len(this%s)==len(that%s)) return + if (.not.len_trim(this%s)==len_trim(that%s)) return + do i=1,len_trim(this%s) + if (.not.(this%s(i:i)==that%s(i:i))) return + end do + end if + + ! All checks passed + string_is_same = .true. + +end function string_is_same + +!> Check that two allocatable string _object_ arrays are exactly identical +pure logical function string_arrays_same(this,that) + !> two string arrays to be compared + type(string_t), allocatable, intent(in) :: this(:), that(:) + + integer :: i + + string_arrays_same = .false. + + if (allocated(this).neqv.allocated(that)) return + if (allocated(this)) then + if (.not.(size(this)==size(that))) return + if (.not.(ubound(this,1)==ubound(that,1))) return + if (.not.(lbound(this,1)==lbound(that,1))) return + do i=lbound(this,1),ubound(this,1) + if (.not.string_is_same(this(i),that(i))) return + end do + end if + + ! All checks passed + string_arrays_same = .true. + +end function string_arrays_same + !> !!### NAME !! notabs(3f) - [fpm_strings:NONALPHA] expand tab characters From a519fb572562742a94ac4e68f8f74c74f57fb8cd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 13:52:36 +0200 Subject: [PATCH 273/799] `FPM_SCOPE` and `FPM_UNIT`: standardize labels --- src/fpm_model.f90 | 75 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index dba15a8161..d19b9d0f7d 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -38,7 +38,6 @@ module fpm_model use iso_fortran_env, only: int64 use fpm_compiler, only: compiler_t, archiver_t, debug use fpm_dependency, only: dependency_tree_t -use fpm_strings, only: string_t, str, len_trim implicit none private @@ -249,23 +248,7 @@ function info_srcfile(source) result(s) ! character(:), allocatable :: exe_name s = s // ', exe_name="' // source%exe_name // '"' ! integer :: unit_scope = FPM_SCOPE_UNKNOWN - s = s // ", unit_scope=" - select case(source%unit_scope) - case (FPM_SCOPE_UNKNOWN) - s = s // "FPM_SCOPE_UNKNOWN" - case (FPM_SCOPE_LIB) - s = s // "FPM_SCOPE_LIB" - case (FPM_SCOPE_DEP) - s = s // "FPM_SCOPE_DEP" - case (FPM_SCOPE_APP) - s = s // "FPM_SCOPE_APP" - case (FPM_SCOPE_TEST) - s = s // "FPM_SCOPE_TEST" - case (FPM_SCOPE_EXAMPLE) - s = s // "FPM_SCOPE_EXAMPLE" - case default - s = s // "INVALID" - end select + s = s // ', unit_scope="' // FPM_SCOPE_NAME(source%unit_scope) // '"' ! type(string_t), allocatable :: modules_provided(:) s = s // ", modules_provided=[" do i = 1, size(source%modules_provided) @@ -280,27 +263,7 @@ function info_srcfile(source) result(s) end do s = s // "]" ! integer :: unit_type = FPM_UNIT_UNKNOWN - s = s // ", unit_type=" - select case(source%unit_type) - case (FPM_UNIT_UNKNOWN) - s = s // "FPM_UNIT_UNKNOWN" - case (FPM_UNIT_PROGRAM) - s = s // "FPM_UNIT_PROGRAM" - case (FPM_UNIT_MODULE) - s = s // "FPM_UNIT_MODULE" - case (FPM_UNIT_SUBMODULE) - s = s // "FPM_UNIT_SUBMODULE" - case (FPM_UNIT_SUBPROGRAM) - s = s // "FPM_UNIT_SUBPROGRAM" - case (FPM_UNIT_CSOURCE) - s = s // "FPM_UNIT_CSOURCE" - case (FPM_UNIT_CPPSOURCE) - s = s // "FPM_UNIT_CPPSOURCE" - case (FPM_UNIT_CHEADER) - s = s // "FPM_UNIT_CHEADER" - case default - s = s // "INVALID" - end select + s = s // ', unit_type="' // FPM_UNIT_NAME(source%unit_type) // '"' ! type(string_t), allocatable :: modules_used(:) s = s // ", modules_used=[" do i = 1, size(source%modules_used) @@ -396,4 +359,38 @@ subroutine show_model(model) print *, info_model(model) end subroutine show_model +!> Return the character name of a scope flag +function FPM_SCOPE_NAME(flag) result(name) + integer, intent(in) :: flag + character(len=:), allocatable :: name + + select case (flag) + case (FPM_SCOPE_UNKNOWN); name = "FPM_SCOPE_UNKNOWN" + case (FPM_SCOPE_LIB); name = "FPM_SCOPE_LIB" + case (FPM_SCOPE_DEP); name = "FPM_SCOPE_DEP" + case (FPM_SCOPE_APP); name = "FPM_SCOPE_APP" + case (FPM_SCOPE_TEST); name = "FPM_SCOPE_TEST" + case (FPM_SCOPE_EXAMPLE); name = "FPM_SCOPE_EXAMPLE" + case default; name = "INVALID" + end select +end function FPM_SCOPE_NAME + +!> Return the character name of a unit flag +function FPM_UNIT_NAME(flag) result(name) + integer, intent(in) :: flag + character(len=:), allocatable :: name + + select case (flag) + case (FPM_UNIT_UNKNOWN); name = "FPM_SCOPE_UNKNOWN" + case (FPM_UNIT_PROGRAM); name = "FPM_UNIT_PROGRAM" + case (FPM_UNIT_MODULE); name = "FPM_UNIT_MODULE" + case (FPM_UNIT_SUBMODULE); name = "FPM_UNIT_SUBMODULE" + case (FPM_UNIT_SUBPROGRAM); name = "FPM_UNIT_SUBPROGRAM" + case (FPM_UNIT_CSOURCE); name = "FPM_UNIT_CSOURCE" + case (FPM_UNIT_CPPSOURCE); name = "FPM_UNIT_CPPSOURCE" + case (FPM_UNIT_CHEADER); name = "FPM_UNIT_CHEADER" + case default; name = "INVALID" + end select +end function FPM_UNIT_NAME + end module fpm_model From bef96c5a06899f8c3e32b6b6d68ae87da04b3a86 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 14:00:54 +0200 Subject: [PATCH 274/799] `FPM_SCOPE` and `FPM_UNIT` parsers --- src/fpm_model.f90 | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index d19b9d0f7d..7c4515db28 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -38,6 +38,7 @@ module fpm_model use iso_fortran_env, only: int64 use fpm_compiler, only: compiler_t, archiver_t, debug use fpm_dependency, only: dependency_tree_t +use fpm_strings, only: string_t, str, len_trim, lower implicit none private @@ -375,6 +376,27 @@ function FPM_SCOPE_NAME(flag) result(name) end select end function FPM_SCOPE_NAME +!> Parse git FPM_SCOPE identifier from a string +integer function parse_scope(name) result(scope) + character(len=*), intent(in) :: name + + character(len=len(name)) :: lowercase + + !> Make it Case insensitive + lowercase = lower(name) + + select case (trim(lowercase)) + case ("FPM_SCOPE_UNKNOWN"); scope = FPM_SCOPE_UNKNOWN + case ("FPM_SCOPE_LIB"); scope = FPM_SCOPE_LIB + case ("FPM_SCOPE_DEP"); scope = FPM_SCOPE_DEP + case ("FPM_SCOPE_APP"); scope = FPM_SCOPE_APP + case ("FPM_SCOPE_TEST"); scope = FPM_SCOPE_TEST + case ("FPM_SCOPE_EXAMPLE"); scope = FPM_SCOPE_EXAMPLE + case default; scope = -9999 + end select + +end function parse_scope + !> Return the character name of a unit flag function FPM_UNIT_NAME(flag) result(name) integer, intent(in) :: flag @@ -393,4 +415,27 @@ function FPM_UNIT_NAME(flag) result(name) end select end function FPM_UNIT_NAME +!> Parse git FPM_UNIT identifier from a string +integer function parse_unit(name) result(unit) + character(len=*), intent(in) :: name + + character(len=len(name)) :: lowercase + + !> Make it Case insensitive + lowercase = lower(name) + + select case (trim(lowercase)) + case ("FPM_UNIT_UNKNOWN"); unit = FPM_UNIT_UNKNOWN + case ("FPM_UNIT_PROGRAM"); unit = FPM_UNIT_PROGRAM + case ("FPM_UNIT_MODULE"); unit = FPM_UNIT_MODULE + case ("FPM_UNIT_SUBMODULE"); unit = FPM_UNIT_SUBMODULE + case ("FPM_UNIT_SUBPROGRAM"); unit = FPM_UNIT_SUBPROGRAM + case ("FPM_UNIT_CSOURCE"); unit = FPM_UNIT_CSOURCE + case ("FPM_UNIT_CPPSOURCE"); unit = FPM_UNIT_CPPSOURCE + case ("FPM_UNIT_CHEADER"); unit = FPM_UNIT_CHEADER + case default; unit = -9999 + end select + +end function parse_unit + end module fpm_model From 9c5888a525790581d565463270d0cc52bf5e2b11 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 14:40:45 +0200 Subject: [PATCH 275/799] `string_t` array: implement and test toml `set_list` --- src/fpm/toml.f90 | 62 ++++++++++++++++++++++++++++- test/fpm_test/test_toml.f90 | 78 ++++++++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 2 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 246bfc27a4..d0be9743b5 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -23,7 +23,7 @@ module fpm_toml public :: read_package_file, toml_table, toml_array, toml_key, toml_stat, & get_value, set_value, get_list, new_table, add_table, add_array, len, & - toml_error, toml_serialize, toml_load, check_keys + toml_error, toml_serialize, toml_load, check_keys, set_list !> An abstract interface for any fpm class that should be fully serializable to/from TOML/JSON type, abstract, public :: serializable_t @@ -293,6 +293,66 @@ subroutine get_list(table, key, list, error) end subroutine get_list + ! Set string array + subroutine set_list(table, key, list, error) + + !> Instance of the string array + type(string_t), allocatable, intent(in) :: list(:) + + !> Key to save to + character(len=*), intent(in) :: key + + !> Instance of the toml table + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + integer :: stat, ilist + type(toml_array), pointer :: children + character(len=:), allocatable :: str + + !> Set no key if array is not present + if (.not.allocated(list)) return + + !> Check the key is not empty + if (len_trim(key)<=0) then + call fatal_error(error, 'key is empty dumping string array to TOML table') + return + end if + + if (size(list)/=1) then ! includes empty list case + + !> String array + call add_array(table, key, children, stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Cannot set array table in "//key//" field") + return + end if + + do ilist = 1, size(list) + call set_value(children, ilist, list(ilist)%s, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Cannot store array entry in "//key//" field") + return + end if + end do + + else + + ! Single value: set string + call set_value(table, key, list(1)%s, stat=stat) + + if (stat /= toml_stat%success) & + call fatal_error(error, "Cannot store entry in "//key//" field") + + return + end if + + end subroutine set_list + + !> Check if table contains only keys that are part of the list. If a key is !> found that is not part of the list, an error is allocated. subroutine check_keys(table, valid_keys, error) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 8d3774dff8..20f63b5259 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -7,6 +7,7 @@ module test_toml & new_dependency_node, new_dependency_tree, resize use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version + use fpm_strings, only: string_t, operator(==), split implicit none private @@ -30,7 +31,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-git-target", git_target_roundtrip), & & new_unittest("serialize-dependency-config", dependency_config_roundtrip), & & new_unittest("serialize-dependency-node", dependency_node_roundtrip), & - & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip)] + & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & + & new_unittest("serialize-string-array", string_array_roundtrip)] end subroutine collect_toml @@ -345,4 +347,78 @@ subroutine dependency_tree_roundtrip(error) end subroutine dependency_tree_roundtrip + !> Test serialization/deserialization of a string array + subroutine string_array_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=*), parameter :: lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing " & + & //"elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad " & + & //"minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo " & + & //"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " & + & //"dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt " & + & //"in culpa qui officia deserunt mollit anim id est laborum" + + integer :: ii, nword + character(len=:), allocatable :: tokens(:) + type(string_t), allocatable :: list(:),copy(:) + type(toml_table) :: table + character(len=16) :: key + + call split(lorem, tokens) + nword = size(tokens) + + !> Convert to string_t array + allocate(list(nword)) + do ii = 1, nword + list(ii) = string_t(trim(tokens(ii))) + end do + + ! Test list with any length + do ii = nword, 1, -1 + + ! Shorten list + list = list(1:ii) + + ! Set list to table + table = toml_table() + + call set_list(table, key="lorem-ipsum", list=list, error=error) + if (allocated(error)) return + + ! Load list from table + call get_list(table, key="lorem-ipsum", list=copy, error=error) + if (allocated(error)) return + + if (.not.(list==copy)) then + call fatal_error(error,'string_array is not equal after TOML roundtrip') + return + end if + + end do + + ! Test empty list + deallocate(list) + allocate(list(0)) + ! Set list to table + table = toml_table() + + call set_list(table, key="lorem-ipsum", list=list, error=error) + if (allocated(error)) return + + ! Load list from table + call get_list(table, key="lorem-ipsum", list=copy, error=error) + if (allocated(error)) return + + if (.not.(list==copy)) then + call fatal_error(error,'empty string_array is not equal after TOML roundtrip') + return + end if + + 1 format('word_',i0) + + + end subroutine string_array_roundtrip + end module test_toml From 5c9b3bec12632e52df7843b3354dc43ab887e813 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 15:15:24 +0200 Subject: [PATCH 276/799] strings: add uppercase function --- src/fpm_strings.f90 | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index d164f76ad2..20b004b1c0 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -39,7 +39,7 @@ module fpm_strings implicit none private -public :: f_string, lower, split, str_ends_with, string_t, str_begins_with_str +public :: f_string, lower, upper, split, str_ends_with, string_t, str_begins_with_str public :: to_fortran_name, is_fortran_name public :: string_array_contains, string_cat, len_trim, operator(.in.), fnv_1a public :: replace, resize, str, join, glob @@ -274,6 +274,37 @@ elemental pure function lower(str,begin,end) result (string) end function lower + !!License: Public Domain + !! Changes a string to upprtcase over optional specified column range +elemental pure function upper(str,begin,end) result (string) + + character(*), intent(In) :: str + character(len(str)) :: string + integer,intent(in),optional :: begin, end + integer :: i + integer :: ibegin, iend + string = str + + ibegin = 1 + if (present(begin))then + ibegin = max(ibegin,begin) + endif + + iend = len_trim(str) + if (present(end))then + iend= min(iend,end) + endif + + do i = ibegin, iend ! step thru each letter in the string in specified range + select case (str(i:i)) + case ('a':'z') + string(i:i) = char(iachar(str(i:i))-32) ! change letter to capitalized + case default + end select + end do + +end function upper + !> Helper function to generate a new string_t instance !> (Required due to the allocatable component) function new_string_t(s) result(string) From 9c348b1a08712f7b8e06ad0940e5add5c31cc71f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 15:15:51 +0200 Subject: [PATCH 277/799] `string_t` array: add deallocated array test --- test/fpm_test/test_toml.f90 | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 20f63b5259..c847dce22e 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -416,8 +416,23 @@ subroutine string_array_roundtrip(error) return end if - 1 format('word_',i0) + ! Test unallocated list + deallocate(list) + table = toml_table() + + call set_list(table, key="lorem-ipsum", list=list, error=error) + if (allocated(error)) return + ! Load list from table + call get_list(table, key="lorem-ipsum", list=copy, error=error) + if (allocated(error)) return + + if (.not.(list==copy)) then + call fatal_error(error,'deallocated string_array is not equal after TOML roundtrip') + return + end if + + 1 format('word_',i0) end subroutine string_array_roundtrip From 1d9e4aef143d5b41e5fdc7385659adff79c594a3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 15:16:13 +0200 Subject: [PATCH 278/799] `srcfile_t`: implement `serializable_t` --- src/fpm_model.f90 | 159 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 9 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index 7c4515db28..a97212bf61 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -38,7 +38,9 @@ module fpm_model use iso_fortran_env, only: int64 use fpm_compiler, only: compiler_t, archiver_t, debug use fpm_dependency, only: dependency_tree_t -use fpm_strings, only: string_t, str, len_trim, lower +use fpm_strings, only: string_t, str, len_trim, upper, operator(==) +use fpm_toml, only: serializable_t, toml_table, toml_stat, set_value, set_list, get_value, get_list +use fpm_error, only: error_t, fatal_error implicit none private @@ -93,7 +95,7 @@ module fpm_model end type fortran_features_t !> Type for describing a source file -type srcfile_t +type, extends(serializable_t) :: srcfile_t !> File path relative to cwd character(:), allocatable :: file_name @@ -124,6 +126,14 @@ module fpm_model !> Current hash integer(int64) :: digest + contains + + !> Serialization interface + procedure :: serializable_is_same => srcfile_is_same + procedure :: dump_to_toml => srcfile_dump_to_toml + procedure :: load_from_toml => srcfile_load_from_toml + + end type srcfile_t @@ -380,12 +390,12 @@ end function FPM_SCOPE_NAME integer function parse_scope(name) result(scope) character(len=*), intent(in) :: name - character(len=len(name)) :: lowercase + character(len=len(name)) :: uppercase !> Make it Case insensitive - lowercase = lower(name) + uppercase = upper(name) - select case (trim(lowercase)) + select case (trim(uppercase)) case ("FPM_SCOPE_UNKNOWN"); scope = FPM_SCOPE_UNKNOWN case ("FPM_SCOPE_LIB"); scope = FPM_SCOPE_LIB case ("FPM_SCOPE_DEP"); scope = FPM_SCOPE_DEP @@ -403,7 +413,7 @@ function FPM_UNIT_NAME(flag) result(name) character(len=:), allocatable :: name select case (flag) - case (FPM_UNIT_UNKNOWN); name = "FPM_SCOPE_UNKNOWN" + case (FPM_UNIT_UNKNOWN); name = "FPM_UNIT_UNKNOWN" case (FPM_UNIT_PROGRAM); name = "FPM_UNIT_PROGRAM" case (FPM_UNIT_MODULE); name = "FPM_UNIT_MODULE" case (FPM_UNIT_SUBMODULE); name = "FPM_UNIT_SUBMODULE" @@ -419,12 +429,12 @@ end function FPM_UNIT_NAME integer function parse_unit(name) result(unit) character(len=*), intent(in) :: name - character(len=len(name)) :: lowercase + character(len=len(name)) :: uppercase !> Make it Case insensitive - lowercase = lower(name) + uppercase = upper(name) - select case (trim(lowercase)) + select case (trim(uppercase)) case ("FPM_UNIT_UNKNOWN"); unit = FPM_UNIT_UNKNOWN case ("FPM_UNIT_PROGRAM"); unit = FPM_UNIT_PROGRAM case ("FPM_UNIT_MODULE"); unit = FPM_UNIT_MODULE @@ -438,4 +448,135 @@ integer function parse_unit(name) result(unit) end function parse_unit +!> Check that two source files are equal +logical function srcfile_is_same(this,that) + class(srcfile_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + srcfile_is_same = .false. + + select type (other=>that) + type is (srcfile_t) + + if (.not.(this%file_name==other%file_name)) return + if (.not.(this%exe_name==other%exe_name)) return + if (.not.(this%unit_scope==other%unit_scope)) return + if (.not.(this%modules_provided==other%modules_provided)) return + if (.not.(this%unit_type==other%unit_type)) return + if (.not.(this%parent_modules==other%parent_modules)) return + if (.not.(this%modules_used==other%modules_used)) return + if (.not.(this%include_dependencies==other%include_dependencies)) return + if (.not.(this%link_libraries==other%link_libraries)) return + if (.not.(this%digest==other%digest)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + srcfile_is_same = .true. + +end function srcfile_is_same + +!> Dump dependency to toml table +subroutine srcfile_dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(srcfile_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + if (allocated(self%file_name)) then + call set_value(table, "file-name", self%file_name, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'srcfile_t: cannot set file-name in TOML table') + return + end if + endif + + if (allocated(self%exe_name)) then + call set_value(table, "exe-name", self%exe_name, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'srcfile_t: cannot set exe-name in TOML table') + return + end if + endif + + call set_value(table,"digest",self%digest) + + ! unit_scope and unit_type are saved as strings so the output is independent + ! of the internal representation + call set_value(table,"unit-scope",FPM_SCOPE_NAME(self%unit_scope)) + call set_value(table,"unit-type",FPM_UNIT_NAME(self%unit_type)) + + call set_list(table,"modules-provided",self%modules_provided, error) + if (allocated(error)) return + + call set_list(table,"parent-modules",self%parent_modules, error) + if (allocated(error)) return + + call set_list(table,"modules-used",self%modules_used, error) + if (allocated(error)) return + + call set_list(table,"include-dependencies",self%include_dependencies, error) + if (allocated(error)) return + + call set_list(table,"link-libraries",self%link_libraries, error) + if (allocated(error)) return + + +end subroutine srcfile_dump_to_toml + +!> Read dependency from toml table (no checks made at this stage) +subroutine srcfile_load_from_toml(self, table, error) + + !> Instance of the serializable object + class(srcfile_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: flag + + call get_value(table, "file-name", self%file_name) + call get_value(table, "exe-name", self%exe_name) + call get_value(table, "digest", self%digest) + + ! unit_scope and unit_type are saved as strings so the output is independent + ! of the internal representation + call get_value(table, "unit-scope", flag) + if (allocated(flag)) self%unit_scope = parse_scope(flag) + call get_value(table, "unit-type", flag) + if (allocated(flag)) self%unit_type = parse_unit(flag) + + call get_list(table,"modules-provided",self%modules_provided, error) + if (allocated(error)) return + + call get_list(table,"parent-modules",self%parent_modules, error) + if (allocated(error)) return + + call get_list(table,"modules-used",self%modules_used, error) + if (allocated(error)) return + + call get_list(table,"include-dependencies",self%include_dependencies, error) + if (allocated(error)) return + + call get_list(table,"link-libraries",self%link_libraries, error) + if (allocated(error)) return + + + +end subroutine srcfile_load_from_toml + + end module fpm_model From 413c723e0d372a40a03b406319864b435b95bf97 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 15:16:22 +0200 Subject: [PATCH 279/799] test `srcfile_t` serialization --- test/fpm_test/test_source_parsing.f90 | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/fpm_test/test_source_parsing.f90 b/test/fpm_test/test_source_parsing.f90 index b480e76c33..45fc5c6474 100644 --- a/test/fpm_test/test_source_parsing.f90 +++ b/test/fpm_test/test_source_parsing.f90 @@ -117,6 +117,8 @@ subroutine test_modules_used(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_modules_used @@ -184,6 +186,8 @@ subroutine test_intrinsic_modules_used(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_intrinsic_modules_used @@ -241,6 +245,8 @@ subroutine test_include_stmt(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_include_stmt !> Try to parse a simple fortran program @@ -296,6 +302,8 @@ subroutine test_program(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_program @@ -378,6 +386,8 @@ subroutine test_module(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_module @@ -427,6 +437,8 @@ subroutine test_module_with_subprogram(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_module_with_subprogram @@ -492,6 +504,8 @@ subroutine test_module_end_stmt(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_module_end_stmt @@ -540,6 +554,8 @@ subroutine test_module_with_c_api(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_module_with_c_api @@ -603,6 +619,8 @@ subroutine test_program_with_module(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_program_with_module @@ -660,6 +678,8 @@ subroutine test_submodule(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_submodule @@ -717,6 +737,8 @@ subroutine test_submodule_ancestor(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_submodule_ancestor @@ -765,6 +787,8 @@ subroutine test_subprogram(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_subprogram @@ -835,6 +859,8 @@ subroutine test_csource(error) return end if + call f_source%test_serialization('srcfile_t: serialization', error) + end subroutine test_csource !> Try to parse fortran program with invalid use statement From c27febfd630b94f8bc51b5f56bb488bd6501d072 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 16:58:57 +0200 Subject: [PATCH 280/799] extend `package_t` --- src/fpm_model.f90 | 88 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index a97212bf61..f7ad0d8c3b 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -92,6 +92,7 @@ module fpm_model !> Form to use for all Fortran sources character(:), allocatable :: source_form + end type fortran_features_t !> Type for describing a source file @@ -133,12 +134,11 @@ module fpm_model procedure :: dump_to_toml => srcfile_dump_to_toml procedure :: load_from_toml => srcfile_load_from_toml - end type srcfile_t !> Type for describing a single package -type package_t +type, extends(serializable_t) :: package_t !> Name of package character(:), allocatable :: name @@ -161,6 +161,13 @@ module fpm_model !> Language features type(fortran_features_t) :: features + contains + + !> Serialization interface + procedure :: serializable_is_same => package_is_same + procedure :: dump_to_toml => package_dump_to_toml + procedure :: load_from_toml => package_load_from_toml + end type package_t @@ -578,5 +585,82 @@ subroutine srcfile_load_from_toml(self, table, error) end subroutine srcfile_load_from_toml +!> Check that two package objects are equal +logical function package_is_same(this,that) + class(package_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + integer :: ii + + package_is_same = .false. + + select type (other=>that) + type is (package_t) + + if (.not.(this%name==other%name)) return + if (.not.(allocated(this%sources).eqv.allocated(other%sources))) return + if (allocated(this%sources)) then + if (.not.(size(this%sources)==size(other%sources))) return + do ii = 1, size(this%sources) + if (.not.(this%sources(ii)==other%sources(ii))) return + end do + end if + + if (.not.(this%macros==other%macros)) return + if (.not.(this%version==other%version)) return + + !> Module naming + if (.not.(this%enforce_module_names.eqv.other%enforce_module_names)) return + if (.not.(this%module_prefix==other%module_prefix)) return + + !> Fortran features + if (.not.(this%features%implicit_typing.eqv.other%features%implicit_typing)) return + if (.not.(this%features%implicit_external.eqv.other%features%implicit_external)) return + if (.not.(this%features%source_form==other%features%source_form)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + package_is_same = .true. + +end function package_is_same + +!> Dump dependency to toml table +subroutine package_dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(package_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call fatal_error(error,' not yet implemented ' ) + +end subroutine package_dump_to_toml + +!> Read dependency from toml table (no checks made at this stage) +subroutine package_load_from_toml(self, table, error) + + !> Instance of the serializable object + class(package_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: flag + + call fatal_error(error, ' not yet implemented ') + +end subroutine package_load_from_toml + end module fpm_model From b649d2df84fe239962994b0cb112cb04edb4e368 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 17:01:57 +0200 Subject: [PATCH 281/799] extend `fortran_features_t` --- src/fpm_model.f90 | 71 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index f7ad0d8c3b..235496d397 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -82,7 +82,7 @@ module fpm_model integer, parameter :: FPM_SCOPE_EXAMPLE = 5 !> Enabled Fortran language features -type :: fortran_features_t +type, extends(serializable_t) :: fortran_features_t !> Use default implicit typing logical :: implicit_typing = .false. @@ -93,6 +93,13 @@ module fpm_model !> Form to use for all Fortran sources character(:), allocatable :: source_form + contains + + !> Serialization interface + procedure :: serializable_is_same => fft_is_same + procedure :: dump_to_toml => fft_dump_to_toml + procedure :: load_from_toml => fft_load_from_toml + end type fortran_features_t !> Type for describing a source file @@ -585,6 +592,64 @@ subroutine srcfile_load_from_toml(self, table, error) end subroutine srcfile_load_from_toml +!> Check that two fortran feature objects are equal +logical function fft_is_same(this,that) + class(fortran_features_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + fft_is_same = .false. + + select type (other=>that) + type is (fortran_features_t) + + if (.not.(this%implicit_typing.eqv.other%implicit_typing)) return + if (.not.(this%implicit_external.eqv.other%implicit_external)) return + if (.not.(this%source_form==other%source_form)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + fft_is_same = .true. + +end function fft_is_same + +!> Dump fortran features to toml table +subroutine fft_dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(fortran_features_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call fatal_error(error,' fortran-features-t serialization not yet implemented ' ) + +end subroutine fft_dump_to_toml + +!> Read dependency from toml table (no checks made at this stage) +subroutine fft_load_from_toml(self, table, error) + + !> Instance of the serializable object + class(fortran_features_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: flag + + call fatal_error(error, ' fortran-features-t serialization not yet implemented ') + +end subroutine fft_load_from_toml + !> Check that two package objects are equal logical function package_is_same(this,that) class(package_t), intent(in) :: this @@ -614,9 +679,7 @@ logical function package_is_same(this,that) if (.not.(this%module_prefix==other%module_prefix)) return !> Fortran features - if (.not.(this%features%implicit_typing.eqv.other%features%implicit_typing)) return - if (.not.(this%features%implicit_external.eqv.other%features%implicit_external)) return - if (.not.(this%features%source_form==other%features%source_form)) return + if (.not.(this%features==other%features)) return class default ! Not the same type From f0201c1a81fb85dac04c8dc5235f2d2aae028231 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 17:24:10 +0200 Subject: [PATCH 282/799] `fortran_features_t`: implement serialization and test --- src/fpm_model.f90 | 41 +++++++++++++++++++++++++++++++++---- test/fpm_test/test_toml.f90 | 23 ++++++++++++++++++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index 235496d397..ee8692f5b6 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -588,8 +588,6 @@ subroutine srcfile_load_from_toml(self, table, error) call get_list(table,"link-libraries",self%link_libraries, error) if (allocated(error)) return - - end subroutine srcfile_load_from_toml !> Check that two fortran feature objects are equal @@ -628,7 +626,27 @@ subroutine fft_dump_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - call fatal_error(error,' fortran-features-t serialization not yet implemented ' ) + integer :: ierr + + call set_value(table, "implicit-typing", self%implicit_typing, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fortran_features_t: cannot set implicit-typing in TOML table') + return + end if + + call set_value(table, "implicit-external", self%implicit_external, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fortran_features_t: cannot set implicit-external in TOML table') + return + end if + + if (allocated(self%source_form)) then + call set_value(table, "source-form", self%source_form, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fortran_features_t: cannot set source-form in TOML table') + return + end if + endif end subroutine fft_dump_to_toml @@ -646,7 +664,22 @@ subroutine fft_load_from_toml(self, table, error) character(len=:), allocatable :: flag - call fatal_error(error, ' fortran-features-t serialization not yet implemented ') + integer :: ierr + + call get_value(table, "implicit-typing", self%implicit_typing, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fortran_features_t: cannot read implicit-typing from TOML table') + return + end if + + call get_value(table, "implicit-external", self%implicit_external, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fortran_features_t: cannot read implicit-typing from TOML table') + return + end if + + ! Return unallocated value if not present + call get_value(table, "source-form", self%source_form) end subroutine fft_load_from_toml diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index c847dce22e..317ed906ab 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -8,6 +8,7 @@ module test_toml use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split + use fpm_model, only: fortran_features_t implicit none private @@ -32,7 +33,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-dependency-config", dependency_config_roundtrip), & & new_unittest("serialize-dependency-node", dependency_node_roundtrip), & & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & - & new_unittest("serialize-string-array", string_array_roundtrip)] + & new_unittest("serialize-string-array", string_array_roundtrip), & + & new_unittest("serialize-fortran-features", fft_roundtrip)] end subroutine collect_toml @@ -436,4 +438,23 @@ subroutine string_array_roundtrip(error) end subroutine string_array_roundtrip + !> Test serialization/deserialization of a fortran-features structure + subroutine fft_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(fortran_features_t) :: fortran + + !> Default object + call fortran%test_serialization('fortran_features_t: default object',error) + if (allocated(error)) return + + !> Set form + fortran%source_form = "free" + call fortran%test_serialization('fortran_features_t: with form',error) + if (allocated(error)) return + + end subroutine fft_roundtrip + end module test_toml From d7f2f10918f42c7bb82cbd8b2459cb7459dd9054 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 18:35:17 +0200 Subject: [PATCH 283/799] set_string wrappers to reduce verbosity --- src/fpm/toml.f90 | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index d0be9743b5..5359edf4ea 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -23,7 +23,7 @@ module fpm_toml public :: read_package_file, toml_table, toml_array, toml_key, toml_stat, & get_value, set_value, get_list, new_table, add_table, add_array, len, & - toml_error, toml_serialize, toml_load, check_keys, set_list + toml_error, toml_serialize, toml_load, check_keys, set_list, set_string !> An abstract interface for any fpm class that should be fully serializable to/from TOML/JSON type, abstract, public :: serializable_t @@ -51,6 +51,11 @@ module fpm_toml end type serializable_t + interface set_string + module procedure set_character + module procedure set_string_type + end interface set_string + abstract interface @@ -352,6 +357,65 @@ subroutine set_list(table, key, list, error) end subroutine set_list + !> Function wrapper to set a character(len=:), allocatable variable to a toml table + subroutine set_character(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> List of keys to check. + character(len=*), intent(in) :: key + + !> The character variable + character(len=:), allocatable, intent(in) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + !> Check the key is not empty + if (len_trim(key)<=0) then + call fatal_error(error, 'key is empty setting character string to TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + if (allocated(var)) then + call set_value(table, key, var, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot set character key <'//key//'> in TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + endif + + end subroutine set_character + + !> Function wrapper to set a character(len=:), allocatable variable to a toml table + subroutine set_string_type(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> List of keys to check. + character(len=*), intent(in) :: key + + !> The character variable + type(string_t), intent(in) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + call set_character(table, key, var%s, error, whereAt) + + end subroutine set_string_type !> Check if table contains only keys that are part of the list. If a key is !> found that is not part of the list, an error is allocated. From 4c02c162c2163a9d966a24c71d20803849b6bac5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 18:36:18 +0200 Subject: [PATCH 284/799] `package_t`: implement serialization --- src/fpm_model.f90 | 129 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 8 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index ee8692f5b6..0ebe907ff0 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -39,12 +39,13 @@ module fpm_model use fpm_compiler, only: compiler_t, archiver_t, debug use fpm_dependency, only: dependency_tree_t use fpm_strings, only: string_t, str, len_trim, upper, operator(==) -use fpm_toml, only: serializable_t, toml_table, toml_stat, set_value, set_list, get_value, get_list +use fpm_toml, only: serializable_t, toml_table, toml_stat, set_value, set_list, get_value, & + & get_list, add_table, toml_key, add_array, set_string use fpm_error, only: error_t, fatal_error implicit none private -public :: fpm_model_t, srcfile_t, show_model, fortran_features_t +public :: fpm_model_t, srcfile_t, show_model, fortran_features_t, package_t public :: FPM_UNIT_UNKNOWN, FPM_UNIT_PROGRAM, FPM_UNIT_MODULE, & FPM_UNIT_SUBMODULE, FPM_UNIT_SUBPROGRAM, FPM_UNIT_CSOURCE, & @@ -160,7 +161,7 @@ module fpm_model character(:), allocatable :: version !> Module naming conventions - logical :: enforce_module_names + logical :: enforce_module_names = .false. !> Prefix for all module names type(string_t) :: module_prefix @@ -662,8 +663,6 @@ subroutine fft_load_from_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: flag - integer :: ierr call get_value(table, "implicit-typing", self%implicit_typing, stat=ierr) @@ -736,7 +735,64 @@ subroutine package_dump_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - call fatal_error(error,' not yet implemented ' ) + integer :: ierr, ii + type(toml_table), pointer :: ptr,this_source + character(16) :: src_name + + call set_string(table, "name", self%name, error, 'package_t') + if (allocated(error)) return + + call set_string(table, "version", self%version, error, 'package_t') + if (allocated(error)) return + + call set_value(table, "module-naming", self%enforce_module_names, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'package_t: cannot set module-naming in TOML table') + return + end if + + call set_string(table, "module-prefix", self%module_prefix, error, 'package_t') + if (allocated(error)) return + + call set_list(table, "macros", self%macros, error) + if (allocated(error)) return + + !> Create a fortran table + call add_table(table, "fortran", ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'package_t: cannot set fortran table in TOML table') + return + end if + call self%features%dump_to_toml(ptr, error) + if (allocated(error)) return + + !> Create a sources table + if (allocated(self%sources)) then + + call add_table(table, "sources", ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'package_t: cannot set sources table in TOML table') + return + end if + + do ii = 1, size(self%sources) + + write(src_name,1) ii + call add_table(ptr, trim(src_name), this_source) + + if (.not. associated(this_source)) then + call fatal_error(error, "package_t cannot create entry for source "//trim(src_name)) + return + end if + + call self%sources(ii)%dump_to_toml(this_source,error) + if (allocated(error)) return + + end do + + end if + + 1 format('src_',i0) end subroutine package_dump_to_toml @@ -752,9 +808,66 @@ subroutine package_load_from_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: flag + integer :: ierr,ii,jj + type(toml_key), allocatable :: keys(:),src_keys(:) + type(toml_table), pointer :: ptr_sources,ptr,ptr_fortran + type(error_t), allocatable :: new_error + + call get_value(table, "name", self%name) + call get_value(table, "version", self%version) + + call get_value(table, "module-naming", self%enforce_module_names, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'package_t: cannot get module-naming from TOML table') + return + end if + + ! Return unallocated value if not present + call get_value(table, "module-prefix", self%module_prefix%s) + + call get_list(table, "macros", self%macros, error) + if (allocated(error)) return + + ! Sources + call table%get_keys(keys) + + find_others: do ii = 1, size(keys) + select case (keys(ii)%key) + case ("fortran") + + call get_value(table, keys(ii), ptr_fortran) + if (.not.associated(ptr_fortran)) then + call fatal_error(error,'package_t: error retrieving fortran table from TOML table') + return + end if + + call self%features%load_from_toml(ptr_fortran,error) + if (allocated(error)) return + + case ("sources") + + call get_value(table, keys(ii), ptr_sources) + if (.not.associated(ptr_sources)) then + call fatal_error(error,'package_t: error retrieving sources table from TOML table') + return + end if + + !> Read all dependencies + call ptr_sources%get_keys(src_keys) + allocate(self%sources(size(src_keys))) + + do jj = 1, size(src_keys) + call get_value(ptr_sources, src_keys(jj), ptr) + call self%sources(jj)%load_from_toml(ptr, error) + if (allocated(error)) return + end do + + case default + cycle find_others + end select + end do find_others - call fatal_error(error, ' not yet implemented ') + call self%dump('tmp_pkg.toml',new_error) end subroutine package_load_from_toml From df02a551b13e39e788fa76dd9c6cda313cddd43a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 18:36:27 +0200 Subject: [PATCH 285/799] `package_t`: test serialization --- test/fpm_test/test_toml.f90 | 92 ++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 317ed906ab..82e3cdcff8 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -2,13 +2,14 @@ module test_toml use testsuite, only : new_unittest, unittest_t, error_t use fpm_toml + use tomlf_constants, only: tf_i8 use fpm_git use fpm_dependency, only: dependency_node_t, destroy_dependency_node, dependency_tree_t, & & new_dependency_node, new_dependency_tree, resize use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split - use fpm_model, only: fortran_features_t + use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE implicit none private @@ -34,7 +35,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-dependency-node", dependency_node_roundtrip), & & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & - & new_unittest("serialize-fortran-features", fft_roundtrip)] + & new_unittest("serialize-fortran-features", fft_roundtrip), & + & new_unittest("serialize-package", package_roundtrip)] end subroutine collect_toml @@ -457,4 +459,90 @@ subroutine fft_roundtrip(error) end subroutine fft_roundtrip + !> Test serialization/deserialization of a package_t structure + subroutine package_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_t) :: pkg + integer :: ierr + + call pkg%dump('pkg.toml',error) + + !> Default object + call pkg%test_serialization('package_t: default object',error) + if (allocated(error)) return + + !> Create a dummy package + pkg%name = "orderpack" + pkg%version = "0.1.0" + pkg%enforce_module_names = .false. + pkg%module_prefix = string_t("") + pkg%features%source_form = "free" + + if (allocated(pkg%sources)) deallocate(pkg%sources) + allocate(pkg%sources(4)) + + pkg%sources(1)%file_name = "build/dependencies/orderpack/src/M_valnth.f90" + pkg%sources(1)%digest = 2662523002405134329_tf_i8 + pkg%sources(1)%unit_scope = FPM_SCOPE_LIB + pkg%sources(1)%unit_type = FPM_UNIT_MODULE + pkg%sources(1)%modules_provided = [string_t("m_valnth")] + deallocate(pkg%sources(1)%parent_modules, stat=ierr) + deallocate(pkg%sources(1)%modules_used, stat=ierr) + deallocate(pkg%sources(1)%include_dependencies, stat=ierr) + deallocate(pkg%sources(1)%link_libraries, stat=ierr) + + pkg%sources(2)%file_name = "build/dependencies/orderpack/src/M_mrgrnk.f90" + pkg%sources(2)%digest = 7985690966656622651_tf_i8 + pkg%sources(2)%unit_scope = FPM_SCOPE_LIB + pkg%sources(2)%unit_type = FPM_UNIT_MODULE + pkg%sources(2)%modules_provided = [string_t("m_mrgrnk")] + pkg%sources(2)%link_libraries = [string_t("netcdf"),string_t("hdf-5")] + deallocate(pkg%sources(2)%parent_modules, stat=ierr) + deallocate(pkg%sources(2)%modules_used, stat=ierr) + deallocate(pkg%sources(2)%include_dependencies, stat=ierr) + deallocate(pkg%sources(2)%link_libraries, stat=ierr) + + pkg%sources(3)%file_name = "build/dependencies/orderpack/src/M_median.f90" + pkg%sources(3)%digest = 7985690966656622651_tf_i8 + pkg%sources(3)%unit_scope = FPM_SCOPE_LIB + pkg%sources(3)%unit_type = FPM_UNIT_MODULE + pkg%sources(3)%modules_provided = [string_t("m_median")] + deallocate(pkg%sources(3)%parent_modules, stat=ierr) + deallocate(pkg%sources(3)%modules_used, stat=ierr) + deallocate(pkg%sources(3)%include_dependencies, stat=ierr) + deallocate(pkg%sources(3)%link_libraries, stat=ierr) + + pkg%sources(4)%file_name = "build/dependencies/orderpack/src/M_unista.f90" + pkg%sources(4)%digest = -7512253540457404792_tf_i8 + pkg%sources(4)%unit_scope = FPM_SCOPE_LIB + pkg%sources(4)%unit_type = FPM_UNIT_MODULE + pkg%sources(4)%modules_provided = [string_t("m_unista")] + pkg%sources(4)%modules_used = [string_t("m_uniinv")] + deallocate(pkg%sources(4)%parent_modules, stat=ierr) + deallocate(pkg%sources(4)%include_dependencies, stat=ierr) + deallocate(pkg%sources(4)%link_libraries, stat=ierr) + + !> Package mock + call pkg%test_serialization('package_t: orderpack',error) + if (allocated(error)) return + + !> Remove some entries + pkg%sources(1)%file_name = "" + pkg%sources(3)%digest = 0 + pkg%sources = pkg%sources(1:3) + call pkg%test_serialization('package_t: orderpack (reduced)',error) + if (allocated(error)) return + + !> Remove all sources + deallocate(pkg%sources,stat=ierr) + call pkg%test_serialization('package_t: no sources',error) + if (allocated(error)) return + + end subroutine package_roundtrip + + + end module test_toml From 3db7bb91b3fb41ca24123e5913e5c691f554a267 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 18:57:20 +0200 Subject: [PATCH 286/799] `archiver_t`: make `serializable_t` and implement test --- src/fpm_compiler.F90 | 129 +++++++++++++++++++++++++++++++++--- test/fpm_test/test_toml.f90 | 30 ++++++++- 2 files changed, 145 insertions(+), 14 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 0b70d3ca2f..d2d4b0e4aa 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -42,7 +42,8 @@ module fpm_compiler & getline, run use fpm_strings, only: split, string_cat, string_t, str_ends_with, str_begins_with_str use fpm_manifest, only : package_config_t -use fpm_error, only: error_t +use fpm_error, only: error_t, fatal_error +use fpm_toml, only: serializable_t, toml_table, set_string, set_value, toml_stat, get_value implicit none public :: compiler_t, new_compiler, archiver_t, new_archiver, get_macros public :: debug @@ -112,7 +113,7 @@ module fpm_compiler !> Definition of archiver object -type :: archiver_t +type, extends(serializable_t) :: archiver_t !> Path to archiver character(len=:), allocatable :: ar !> Use response files to pass arguments @@ -124,6 +125,12 @@ module fpm_compiler contains !> Create static archive procedure :: make_archive + + !> Serialization interface + procedure :: serializable_is_same => ar_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type archiver_t @@ -211,7 +218,7 @@ module fpm_compiler flag_cray_implicit_typing = " -el", & flag_cray_fixed_form = " -ffixed", & flag_cray_free_form = " -ffree" - + contains @@ -440,7 +447,7 @@ pure subroutine set_cpp_preprocessor_flags(id, flags) end subroutine set_cpp_preprocessor_flags -!> This function will parse and read the macros list and +!> This function will parse and read the macros list and !> return them as defined flags. function get_macros(id, macros_list, version) result(macros) integer(compiler_enum), intent(in) :: id @@ -450,7 +457,7 @@ function get_macros(id, macros_list, version) result(macros) character(len=:), allocatable :: macros character(len=:), allocatable :: macro_definition_symbol character(:), allocatable :: valued_macros(:) - + integer :: i @@ -473,10 +480,10 @@ function get_macros(id, macros_list, version) result(macros) end if do i = 1, size(macros_list) - + !> Split the macro name and value. call split(macros_list(i)%s, valued_macros, delimiters="=") - + if (size(valued_macros) > 1) then !> Check if the value of macro starts with '{' character. if (str_begins_with_str(trim(valued_macros(size(valued_macros))), "{")) then @@ -486,15 +493,15 @@ function get_macros(id, macros_list, version) result(macros) !> Check if the string contains "version" as substring. if (index(valued_macros(size(valued_macros)), "version") /= 0) then - + !> These conditions are placed in order to ensure proper spacing between the macros. macros = macros//macro_definition_symbol//trim(valued_macros(1))//'='//version cycle end if end if - end if + end if end if - + macros = macros//macro_definition_symbol//macros_list(i)%s end do @@ -919,7 +926,7 @@ subroutine new_compiler(self, fc, cc, cxx, echo, verbose) logical, intent(in) :: verbose self%id = get_compiler_id(fc) - + self%echo = echo self%verbose = verbose self%fc = fc @@ -1141,5 +1148,105 @@ pure function debug_archiver(self) result(repr) repr = 'ar="'//self%ar//'"' end function debug_archiver +!> Check that two source files are equal +logical function ar_is_same(this,that) + class(archiver_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + ar_is_same = .false. + + select type (other=>that) + type is (archiver_t) + + if (.not.(this%ar==other%ar)) return + if (.not.(this%use_response_file.eqv.other%use_response_file)) return + if (.not.(this%echo.eqv.other%echo)) return + if (.not.(this%verbose.eqv.other%verbose)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + ar_is_same = .true. + +end function ar_is_same + +!> Dump dependency to toml table +subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(archiver_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + !> Path to archiver + call set_string(table, "ar", self%ar, error, 'archiver_t') + if (allocated(error)) return + + call set_value(table, "use-response-file", self%use_response_file, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error dumping use_response_file') + return + end if + + call set_value(table, "echo", self%echo, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error dumping echo') + return + end if + + call set_value(table, "verbose", self%verbose, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error dumping verbose') + return + end if + +end subroutine dump_to_toml + +!> Read dependency from toml table (no checks made at this stage) +subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(archiver_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + call get_value(table, "ar", self%ar) + + call get_value(table, "use-response-file", self%use_response_file, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error getting use_response_file from TOML') + return + end if + + call get_value(table, "echo", self%echo, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error getting echo from TOML') + return + end if + + call get_value(table, "verbose", self%verbose, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error getting verbose from TOML') + return + end if + +end subroutine load_from_toml + + end module fpm_compiler diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 82e3cdcff8..10dee51754 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -10,6 +10,7 @@ module test_toml use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE + use fpm_compiler, only: archiver_t implicit none private @@ -36,7 +37,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & - & new_unittest("serialize-package", package_roundtrip)] + & new_unittest("serialize-package", package_roundtrip), & + & new_unittest("serialize-archiver", ar_roundtrip)] end subroutine collect_toml @@ -468,8 +470,6 @@ subroutine package_roundtrip(error) type(package_t) :: pkg integer :: ierr - call pkg%dump('pkg.toml',error) - !> Default object call pkg%test_serialization('package_t: default object',error) if (allocated(error)) return @@ -543,6 +543,30 @@ subroutine package_roundtrip(error) end subroutine package_roundtrip + !> Test serialization/deserialization of an archiver_t structure + subroutine ar_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(archiver_t) :: ar + integer :: ierr + + call ar%dump('ar.toml',error) + + !> Default object + call ar%test_serialization('archiver_t: default object',error) + if (allocated(error)) return + + !> change a few items + ar%ar = "ar" + ar%echo = .true. + ar%use_response_file = .false. + + call ar%test_serialization('archiver_t: ar',error) + + end subroutine ar_roundtrip + end module test_toml From 2f1e8cf11937b48e45ef27e32301613810774a7b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 19:08:44 +0200 Subject: [PATCH 287/799] `compiler_t`: make `serializable_t` and implement test --- src/fpm_compiler.F90 | 117 +++++++++++++++++++++++++++++++++++- test/fpm_test/test_toml.f90 | 31 ++++++++-- 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index d2d4b0e4aa..b8e3010777 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -75,7 +75,7 @@ module fpm_compiler !> Definition of compiler object -type :: compiler_t +type, extends(serializable_t) :: compiler_t !> Identifier of the compiler integer(compiler_enum) :: id = id_unknown !> Path to the Fortran compiler @@ -109,6 +109,12 @@ module fpm_compiler procedure :: is_unknown !> Enumerate libraries, based on compiler and platform procedure :: enumerate_libraries + + !> Serialization interface + procedure :: serializable_is_same => compiler_is_same + procedure :: dump_to_toml => compiler_dump + procedure :: load_from_toml => compiler_load + end type compiler_t @@ -1148,7 +1154,7 @@ pure function debug_archiver(self) result(repr) repr = 'ar="'//self%ar//'"' end function debug_archiver -!> Check that two source files are equal +!> Check that two archiver_t objects are equal logical function ar_is_same(this,that) class(archiver_t), intent(in) :: this class(serializable_t), intent(in) :: that @@ -1247,6 +1253,113 @@ subroutine load_from_toml(self, table, error) end subroutine load_from_toml +!> Check that two compiler_t objects are equal +logical function compiler_is_same(this,that) + class(compiler_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + compiler_is_same = .false. + + select type (other=>that) + type is (compiler_t) + + if (.not.(this%id==other%id)) return + if (.not.(this%fc==other%fc)) return + if (.not.(this%cc==other%cc)) return + if (.not.(this%cxx==other%cxx)) return + if (.not.(this%echo.eqv.other%echo)) return + if (.not.(this%verbose.eqv.other%verbose)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + compiler_is_same = .true. + +end function compiler_is_same + +!> Dump dependency to toml table +subroutine compiler_dump(self, table, error) + + !> Instance of the serializable object + class(compiler_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + call set_value(table, "id", self%id, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'compiler_t: error dumping id') + return + end if + + call set_string(table, "fc", self%fc, error, 'compiler_t') + if (allocated(error)) return + call set_string(table, "cc", self%cc, error, 'compiler_t') + if (allocated(error)) return + call set_string(table, "cxx", self%cxx, error, 'compiler_t') + if (allocated(error)) return + + call set_value(table, "echo", self%echo, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error dumping echo') + return + end if + + call set_value(table, "verbose", self%verbose, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'archiver_t: error dumping verbose') + return + end if + +end subroutine compiler_dump + +!> Read dependency from toml table (no checks made at this stage) +subroutine compiler_load(self, table, error) + + !> Instance of the serializable object + class(compiler_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr + + call get_value(table, "id", self%id, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'compiler_t: error getting id from TOML') + return + end if + + call get_value(table, "fc", self%fc) + call get_value(table, "cc", self%cc) + call get_value(table, "cxx", self%cxx) + + call get_value(table, "echo", self%echo, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'compiler_t: error getting echo from TOML') + return + end if + + call get_value(table, "verbose", self%verbose, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'compiler_t: error getting verbose from TOML') + return + end if + +end subroutine compiler_load + + end module fpm_compiler diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 10dee51754..800cc0433c 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -10,7 +10,7 @@ module test_toml use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE - use fpm_compiler, only: archiver_t + use fpm_compiler, only: archiver_t, compiler_t, id_gcc implicit none private @@ -38,7 +38,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-package", package_roundtrip), & - & new_unittest("serialize-archiver", ar_roundtrip)] + & new_unittest("serialize-archiver", ar_roundtrip), & + & new_unittest("serialize-compiler", compiler_roundtrip)] end subroutine collect_toml @@ -552,8 +553,6 @@ subroutine ar_roundtrip(error) type(archiver_t) :: ar integer :: ierr - call ar%dump('ar.toml',error) - !> Default object call ar%test_serialization('archiver_t: default object',error) if (allocated(error)) return @@ -568,5 +567,29 @@ subroutine ar_roundtrip(error) end subroutine ar_roundtrip + !> Test serialization/deserialization of a compiler_t structure + subroutine compiler_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(compiler_t) :: compiler + + !> Default object + call compiler%test_serialization('compiler_t: default object',error) + if (allocated(error)) return + + !> change a few items + compiler%id = id_gcc + compiler%fc = "gfortran -ffree-line-length-none -fdefault-real-8 -O3" + compiler%cc = "" + compiler%cxx = "g++ -O3 -std=c++11" + compiler%echo = .false. + + call compiler%dump('compiler.toml',error) + + call compiler%test_serialization('compiler_t: gcc',error) + + end subroutine compiler_roundtrip end module test_toml From 8d211f133df45546db7a9965e338d5e511a19cfe Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 19:19:02 +0200 Subject: [PATCH 288/799] `fpm_model_t`: make `serializable_t`, implement comparison operator --- src/fpm_model.f90 | 91 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index 0ebe907ff0..3a2204d025 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -181,7 +181,7 @@ module fpm_model !> Type describing everything required to build !> the root package and its dependencies. -type :: fpm_model_t +type, extends(serializable_t) :: fpm_model_t !> Name of root package character(:), allocatable :: package_name @@ -231,6 +231,13 @@ module fpm_model !> Prefix for all module names type(string_t) :: module_prefix + contains + + !> Serialization interface + procedure :: serializable_is_same => model_is_same + procedure :: dump_to_toml => model_dump_to_toml + procedure :: load_from_toml => model_load_from_toml + end type fpm_model_t contains @@ -867,9 +874,87 @@ subroutine package_load_from_toml(self, table, error) end select end do find_others - call self%dump('tmp_pkg.toml',new_error) - end subroutine package_load_from_toml +!> Check that two model objects are equal +logical function model_is_same(this,that) + class(fpm_model_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + type(fpm_model_t), pointer :: other + + integer :: ii + + model_is_same = .false. + + select type (other=>that) + type is (fpm_model_t) + + if (.not.(this%package_name==other%package_name)) return + if (.not.(allocated(this%packages).eqv.allocated(other%packages))) return + if (allocated(this%packages)) then + if (.not.(size(this%packages)==size(other%packages))) return + do ii = 1, size(this%packages) + if (.not.(this%packages(ii)==other%packages(ii))) return + end do + end if + + if (.not.(this%compiler==other%compiler)) return + if (.not.(this%archiver==other%archiver)) return + if (.not.(this%fortran_compile_flags==other%fortran_compile_flags)) return + if (.not.(this%c_compile_flags==other%c_compile_flags)) return + if (.not.(this%cxx_compile_flags==other%cxx_compile_flags)) return + if (.not.(this%link_flags==other%link_flags)) return + if (.not.(this%build_prefix==other%build_prefix)) return + if (.not.(this%include_dirs==other%include_dirs)) return + if (.not.(this%link_libraries==other%link_libraries)) return + if (.not.(this%external_modules==other%external_modules)) return + if (.not.(this%deps==other%deps)) return + if (.not.(this%include_tests.eqv.other%include_tests)) return + if (.not.(this%enforce_module_names.eqv.other%enforce_module_names)) return + if (.not.(this%module_prefix==other%module_prefix)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + model_is_same = .true. + +end function model_is_same + +!> Dump dependency to toml table +subroutine model_dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(fpm_model_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call fatal_error(error,' model_t: dump not implemented ') + +end subroutine model_dump_to_toml + +!> Read dependency from toml table (no checks made at this stage) +subroutine model_load_from_toml(self, table, error) + + !> Instance of the serializable object + class(fpm_model_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call fatal_error(error,' model_t: load not implemented ') + +end subroutine model_load_from_toml + end module fpm_model From f46dccfa6604448b25f385f80127d808cfaccbfe Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 19:46:31 +0200 Subject: [PATCH 289/799] `fpm_model_t`: implement serialization --- src/fpm_model.f90 | 196 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 2 deletions(-) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index 3a2204d025..f0593b16b1 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -937,7 +937,103 @@ subroutine model_dump_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - call fatal_error(error,' model_t: dump not implemented ') + integer :: ierr, ii + type(toml_table), pointer :: ptr,ptr_pkg + character(27) :: unnamed + + call set_string(table, "package-name", self%package_name, error, 'fpm_model_t') + if (allocated(error)) return + + call add_table(table, "compiler", ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'package_t: cannot set compiler table') + return + end if + call self%compiler%dump_to_toml(ptr, error) + if (allocated(error)) return + + call add_table(table, "archiver", ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'package_t: cannot set archiver table') + return + end if + call self%archiver%dump_to_toml(ptr, error) + if (allocated(error)) return + + call set_string(table, "fortran-flags", self%fortran_compile_flags, error, 'fpm_model_t') + if (allocated(error)) return + call set_string(table, "c-flags", self%c_compile_flags, error, 'fpm_model_t') + if (allocated(error)) return + call set_string(table, "cxx-flags", self%cxx_compile_flags, error, 'fpm_model_t') + if (allocated(error)) return + call set_string(table, "link-flags", self%link_flags, error, 'fpm_model_t') + if (allocated(error)) return + call set_string(table, "build-prefix", self%build_prefix, error, 'fpm_model_t') + if (allocated(error)) return + call set_list(table, "include-dirs", self%include_dirs, error) + if (allocated(error)) return + call set_list(table, "link-libraries", self%link_libraries, error) + if (allocated(error)) return + call set_list(table, "external-modules", self%external_modules, error) + if (allocated(error)) return + + call set_value(table, "include-tests", self%include_tests, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fpm_model_t: cannot set include-tests in TOML table') + return + end if + + call set_value(table, "module-naming", self%enforce_module_names, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fpm_model_t: cannot set module-naming in TOML table') + return + end if + call set_string(table, "module-prefix", self%module_prefix, error, 'fpm_model_t') + if (allocated(error)) return + + call add_table(table, "deps", ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fpm_model_t: cannot set dependencies table') + return + end if + call self%deps%dump_to_toml(ptr, error) + if (allocated(error)) return + + !> Array of packages (including the root package) + if (allocated(self%packages)) then + + ! Create packages table + call add_table(table, "packages", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, "fpm_model_t cannot create dependency table ") + return + end if + + do ii = 1, size(self%packages) + + associate (pkg => self%packages(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) ii + call add_table(ptr_pkg, trim(unnamed), ptr) + else + call add_table(ptr_pkg, pkg%name, ptr) + end if + if (.not. associated(ptr)) then + call fatal_error(error, "fpm_model_t cannot create entry for package "//pkg%name) + return + end if + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + 1 format('UNNAMED_PACKAGE_',i0) end subroutine model_dump_to_toml @@ -953,7 +1049,103 @@ subroutine model_load_from_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - call fatal_error(error,' model_t: load not implemented ') + type(toml_key), allocatable :: keys(:),pkg_keys(:) + integer :: ierr, ii, jj + type(toml_table), pointer :: ptr,ptr_pkg + character(27) :: unnamed + + call table%get_keys(keys) + + call get_value(table, "package-name", self%package_name) + call get_value(table, "fortran-flags", self%fortran_compile_flags) + call get_value(table, "c-flags", self%c_compile_flags) + call get_value(table, "cxx-flags", self%cxx_compile_flags) + call get_value(table, "link-flags", self%link_flags) + call get_value(table, "build-prefix", self%build_prefix) + + if (allocated(self%packages)) deallocate(self%packages) + sub_deps: do ii = 1, size(keys) + + select case (keys(ii)%key) + case ("compiler") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,'fpm_model_t: error retrieving compiler table') + return + end if + + call self%compiler%load_from_toml(ptr, error) + if (allocated(error)) return + + case ("archiver") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,'fpm_model_t: error retrieving archiver table') + return + end if + + call self%archiver%load_from_toml(ptr, error) + if (allocated(error)) return + + case ("deps") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,'fpm_model_t: error retrieving dependency tree table') + return + end if + + call self%deps%load_from_toml(ptr, error) + if (allocated(error)) return + + case ("packages") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,'fpm_model_t: error retrieving packages table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%packages(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%packages(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + + end do + + + case default + cycle sub_deps + end select + + end do sub_deps + + call get_list(table, "include-dirs", self%include_dirs, error) + if (allocated(error)) return + call get_list(table, "link-libraries", self%link_libraries, error) + if (allocated(error)) return + call get_list(table, "external-modules", self%external_modules, error) + if (allocated(error)) return + + call get_value(table, "include-tests", self%include_tests, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fpm_model_t: cannot read include-tests in TOML table') + return + end if + + call get_value(table, "module-naming", self%enforce_module_names, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'fpm_model_t: cannot set module-naming in TOML table') + return + end if + call get_value(table, "module-prefix", self%module_prefix%s) end subroutine model_load_from_toml From 152faa2ccff2c403abe442d1f7db7d32203761dd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 12 Apr 2023 20:20:38 +0200 Subject: [PATCH 290/799] `fpm_model_t`: implement test (to be reduced) --- test/fpm_test/test_toml.f90 | 1261 ++++++++++++++++++++++++++++++++++- 1 file changed, 1259 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 800cc0433c..d7afad507a 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -9,7 +9,7 @@ module test_toml use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split - use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE + use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t use fpm_compiler, only: archiver_t, compiler_t, id_gcc implicit none @@ -39,7 +39,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-package", package_roundtrip), & & new_unittest("serialize-archiver", ar_roundtrip), & - & new_unittest("serialize-compiler", compiler_roundtrip)] + & new_unittest("serialize-compiler", compiler_roundtrip), & + & new_unittest("serialize-model", fpm_model_roundtrip)] end subroutine collect_toml @@ -592,4 +593,1260 @@ subroutine compiler_roundtrip(error) end subroutine compiler_roundtrip + !> Get a simplified TOML representation of the fpm v0.8.1 model + subroutine fpm_081_table(table) + + !> TOML representation of the fpm v0.8.1 model + type(toml_table), allocatable, intent(out) :: table + + !> simplified TOML representation of the fpm v0.8.1 model + character, parameter :: NL = new_line('a') + character(len=:), allocatable :: fpm + + integer :: iunit + + allocate(character(len=0) :: fpm) + fpm = fpm//NL//'package-name = "fpm"' + fpm = fpm//NL//'fortran-flags = " -Wall -Wextra -fPIC -fmax-errors=1 -g "' + fpm = fpm//NL//'c-flags = ""' + fpm = fpm//NL//'cxx-flags = ""' + fpm = fpm//NL//'link-flags = ""' + fpm = fpm//NL//'build-prefix = "build/gfortran"' + fpm = fpm//NL//'include-dirs = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'external-modules = [ ]' + fpm = fpm//NL//'include-tests = false' + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + fpm = fpm//NL//'[compiler]' + fpm = fpm//NL//'id = 1' + fpm = fpm//NL//'fc = "gfortran"' + fpm = fpm//NL//'cc = "gcc"' + fpm = fpm//NL//'cxx = "g++"' + fpm = fpm//NL//'echo = false' + fpm = fpm//NL//'verbose = false' + fpm = fpm//NL//'[archiver]' + fpm = fpm//NL//'ar = "ar -rs "' + fpm = fpm//NL//'use-response-file = false' + fpm = fpm//NL//'echo = false' + fpm = fpm//NL//'verbose = false' + fpm = fpm//NL//'[deps]' + fpm = fpm//NL//'unit = 6' + fpm = fpm//NL//'verbosity = 1' + fpm = fpm//NL//'dep-dir = "build/dependencies"' + fpm = fpm//NL//'cache = "build/cache.toml"' + fpm = fpm//NL//'ndep = 4' + fpm = fpm//NL//'[deps.dependencies]' + fpm = fpm//NL//'[deps.dependencies.fpm]' + fpm = fpm//NL//'name = "fpm"' + fpm = fpm//NL//'path = "."' + fpm = fpm//NL//'version = "0.8.0"' + fpm = fpm//NL//'proj-dir = "./."' + fpm = fpm//NL//'done = true' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.toml-f]' + fpm = fpm//NL//'name = "toml-f"' + fpm = fpm//NL//'version = "0.4.0"' + fpm = fpm//NL//'proj-dir = "build/dependencies/toml-f"' + fpm = fpm//NL//'revision = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f"' + fpm = fpm//NL//'done = true' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = true' + fpm = fpm//NL//'[deps.dependencies.toml-f.git]' + fpm = fpm//NL//'descriptor = "revision"' + fpm = fpm//NL//'url = "https://github.com/toml-f/toml-f"' + fpm = fpm//NL//'object = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f"' + fpm = fpm//NL//'[deps.dependencies.M_CLI2]' + fpm = fpm//NL//'name = "M_CLI2"' + fpm = fpm//NL//'version = "1.0.0"' + fpm = fpm//NL//'proj-dir = "build/dependencies/M_CLI2"' + fpm = fpm//NL//'revision = "7264878cdb1baff7323cc48596d829ccfe7751b8"' + fpm = fpm//NL//'done = true' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = true' + fpm = fpm//NL//'[deps.dependencies.M_CLI2.git]' + fpm = fpm//NL//'descriptor = "revision"' + fpm = fpm//NL//'url = "https://github.com/urbanjost/M_CLI2.git"' + fpm = fpm//NL//'object = "7264878cdb1baff7323cc48596d829ccfe7751b8"' + fpm = fpm//NL//'[deps.dependencies.jonquil]' + fpm = fpm//NL//'name = "jonquil"' + fpm = fpm//NL//'version = "0.2.0"' + fpm = fpm//NL//'proj-dir = "build/dependencies/jonquil"' + fpm = fpm//NL//'revision = "05d30818bb12fb877226ce284b9a3a41b971a889"' + fpm = fpm//NL//'done = true' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = true' + fpm = fpm//NL//'[deps.dependencies.jonquil.git]' + fpm = fpm//NL//'descriptor = "revision"' + fpm = fpm//NL//'url = "https://github.com/toml-f/jonquil"' + fpm = fpm//NL//'object = "05d30818bb12fb877226ce284b9a3a41b971a889"' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_5]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_6]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_7]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_8]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_9]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_10]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_11]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_12]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_13]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_14]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_15]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_16]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_17]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_18]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_19]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_20]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_21]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_22]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_23]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_24]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_25]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[packages]' + fpm = fpm//NL//'[packages.fpm]' + fpm = fpm//NL//'name = "fpm"' + fpm = fpm//NL//'version = "0.8.0"' + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + fpm = fpm//NL//'[packages.fpm.fortran]' + fpm = fpm//NL//'implicit-typing = false' + fpm = fpm//NL//'implicit-external = false' + fpm = fpm//NL//'source-form = "free"' + fpm = fpm//NL//'[packages.fpm.sources]' + fpm = fpm//NL//'[packages.fpm.sources.src_1]' + fpm = fpm//NL//'file-name = "././src/fpm.f90"' + fpm = fpm//NL//'digest = 4322290725857190613' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_strings", "fpm_backend", "fpm_compiler", "fpm_error" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_2]' + fpm = fpm//NL//'file-name = "././src/fpm_backend.F90"' + fpm = fpm//NL//'digest = -3210121688944515946' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_backend"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_backend_output" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_3]' + fpm = fpm//NL//'file-name = "././src/fpm_environment.f90"' + fpm = fpm//NL//'digest = 2235607720245152632' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_environment"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "fpm_error"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_4]' + fpm = fpm//NL//'file-name = "././src/fpm_model.f90"' + fpm = fpm//NL//'digest = -6774177234665080583' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_model"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_compiler", "fpm_dependency" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_5]' + fpm = fpm//NL//'file-name = "././src/filesystem_utilities.c"' + fpm = fpm//NL//'digest = 4957633104775755438' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_6]' + fpm = fpm//NL//'file-name = "././src/fpm_filesystem.F90"' + fpm = fpm//NL//'digest = 1871084827152368652' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_filesystem"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_environment" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_7]' + fpm = fpm//NL//'file-name = "././src/fpm_strings.f90"' + fpm = fpm//NL//'digest = 7038915013685504829' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_strings"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_8]' + fpm = fpm//NL//'file-name = "././src/fpm_settings.f90"' + fpm = fpm//NL//'digest = -885425387141891996' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_settings"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_environment", "fpm_error" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_9]' + fpm = fpm//NL//'file-name = "././src/fpm_os.c"' + fpm = fpm//NL//'digest = -4523865409175594663' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_10]' + fpm = fpm//NL//'file-name = "././src/fpm_backend_console.f90"' + fpm = fpm//NL//'digest = 1732983699585955966' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_backend_console"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_11]' + fpm = fpm//NL//'file-name = "././src/fpm_source_parsing.f90"' + fpm = fpm//NL//'digest = 6098986130375861226' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_source_parsing"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_model", "fpm_filesystem" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_12]' + fpm = fpm//NL//'file-name = "././src/fpm_os.F90"' + fpm = fpm//NL//'digest = -4743856136050054640' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_os"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_environment", "fpm_error" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_13]' + fpm = fpm//NL//'file-name = "././src/fpm_compiler.F90"' + fpm = fpm//NL//'digest = -2442073797366752057' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_compiler"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_filesystem", "fpm_strings" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_14]' + fpm = fpm//NL//'file-name = "././src/fpm_command_line.f90"' + fpm = fpm//NL//'digest = 7180707928326338392' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_command_line"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_environment", "m_cli2", "m_cli2", "fpm_strings" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_15]' + fpm = fpm//NL//'file-name = "././src/fpm_backend_output.f90"' + fpm = fpm//NL//'digest = 7154367044486334558' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_backend_output"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_targets", "fpm_backend_console" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_16]' + fpm = fpm//NL//'file-name = "././src/fpm_targets.f90"' + fpm = fpm//NL//'digest = -8234965779941208361' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_targets"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_model", "fpm_compiler", "fpm_environment" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_17]' + fpm = fpm//NL//'file-name = "././src/fpm_sources.f90"' + fpm = fpm//NL//'digest = 3391120653956350167' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_sources"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_model", "fpm_filesystem", "fpm_strings" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_18]' + fpm = fpm//NL//'file-name = "././src/ptycheck/iscygpty.c"' + fpm = fpm//NL//'digest = -4887164695298162637' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = "iscygpty.h"' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_19]' + fpm = fpm//NL//'file-name = "././src/ptycheck/isatty.c"' + fpm = fpm//NL//'digest = 6664536934601490990' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = "iscygpty.h"' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_20]' + fpm = fpm//NL//'file-name = "././src/ptycheck/iscygpty.h"' + fpm = fpm//NL//'digest = -3550201113101300999' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_CHEADER"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_21]' + fpm = fpm//NL//'file-name = "././src/fpm/downloader.f90"' + fpm = fpm//NL//'digest = 620358568720613499' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_SUBPROGRAM"' + fpm = fpm//NL//'modules-provided = "fpm_downloader"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_versioning", "jonquil" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_22]' + fpm = fpm//NL//'file-name = "././src/fpm/error.f90"' + fpm = fpm//NL//'digest = 7324399436715753500' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_error"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "fpm_strings"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_23]' + fpm = fpm//NL//'file-name = "././src/fpm/toml.f90"' + fpm = fpm//NL//'digest = 2411620725015864401' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_toml"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "tomlf" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_24]' + fpm = fpm//NL//'file-name = "././src/fpm/installer.f90"' + fpm = fpm//NL//'digest = 581769321360482292' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_installer"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_error", "fpm_filesystem" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_25]' + fpm = fpm//NL//'file-name = "././src/fpm/versioning.f90"' + fpm = fpm//NL//'digest = -1370610786727991294' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_versioning"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "fpm_error"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_26]' + fpm = fpm//NL//'file-name = "././src/fpm/git.f90"' + fpm = fpm//NL//'digest = -7368368636549243157' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_git"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_27]' + fpm = fpm//NL//'file-name = "././src/fpm/dependency.f90"' + fpm = fpm//NL//'digest = -2836785909441977019' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_dependency"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_error", "fpm_filesystem"]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_28]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest.f90"' + fpm = fpm//NL//'digest = -1346850924839827718' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_manifest_build", "fpm_manifest_example" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_29]' + fpm = fpm//NL//'file-name = "././src/fpm/cmd/new.f90"' + fpm = fpm//NL//'digest = 697853208011446608' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_cmd_new"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm_environment" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_30]' + fpm = fpm//NL//'file-name = "././src/fpm/cmd/update.f90"' + fpm = fpm//NL//'digest = -8232305547308400988' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_cmd_update"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm_manifest" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_31]' + fpm = fpm//NL//'file-name = "././src/fpm/cmd/install.f90"' + fpm = fpm//NL//'digest = -6707501025391219376' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_cmd_install"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm", "fpm_backend", "fpm_command_line" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_32]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/test.f90"' + fpm = fpm//NL//'digest = 1399197227023080626' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_test"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_manifest_dependency", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_33]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/profiles.f90"' + fpm = fpm//NL//'digest = -7975317648924650587' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_profile"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_toml", "fpm_strings", "fpm_filesystem" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_34]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/example.f90"' + fpm = fpm//NL//'digest = 2220193652669081694' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_example"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_manifest_dependency", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_35]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/executable.f90"' + fpm = fpm//NL//'digest = 2826537585451151940' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_executable"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_manifest_dependency", "fpm_error", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_36]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/install.f90"' + fpm = fpm//NL//'digest = 6941308343630725905' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_install"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_37]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/package.f90"' + fpm = fpm//NL//'digest = 4046915203104200691' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_package"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_manifest_build", "fpm_manifest_dependency", ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_38]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/preprocess.f90"' + fpm = fpm//NL//'digest = 4463864760686846214' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_mainfest_preprocess"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_39]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/build.f90"' + fpm = fpm//NL//'digest = 7486174362460284832' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_build"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_40]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/dependency.f90"' + fpm = fpm//NL//'digest = -6006235286439662663' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_dependency"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_git", "fpm_versioning" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_41]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/library.f90"' + fpm = fpm//NL//'digest = -1698783511442136567' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_library"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_42]' + fpm = fpm//NL//'file-name = "././src/fpm/manifest/fortran.f90"' + fpm = fpm//NL//'digest = -6768952943164424742' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "fpm_manifest_fortran"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_toml" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_43]' + fpm = fpm//NL//'file-name = "app/main.f90"' + fpm = fpm//NL//'exe-name = "fpm"' + fpm = fpm//NL//'digest = 7759460120440225004' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_APP"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm_os" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_44]' + fpm = fpm//NL//'file-name = "test/help_test/help_test.f90"' + fpm = fpm//NL//'exe-name = "help-test"' + fpm = fpm//NL//'digest = -7601948172740854190' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_environment" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_45]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_os.f90"' + fpm = fpm//NL//'digest = 718441623146001654' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_os"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_filesystem", "fpm_environment", "fpm_os" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_46]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_source_parsing.f90"' + fpm = fpm//NL//'digest = 5852386252678959798' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_SUBPROGRAM"' + fpm = fpm//NL//'modules-provided = "test_source_parsing"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_filesystem", "fpm_strings" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_47]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_settings.f90"' + fpm = fpm//NL//'digest = -3541669032396077479' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_settings"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_settings", "fpm_filesystem", "fpm_os" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_48]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_backend.f90"' + fpm = fpm//NL//'digest = 2723265999281936523' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_backend"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "test_module_dependencies", "fpm_backend" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_49]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_versioning.f90"' + fpm = fpm//NL//'digest = 7879213895027593947' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_versioning"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_versioning" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_50]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_manifest.f90"' + fpm = fpm//NL//'digest = -5417606542127631442' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_manifest"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "testsuite", "fpm_manifest" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_51]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_filesystem.f90"' + fpm = fpm//NL//'digest = -3128825714354096496' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_filesystem"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_filesystem", "fpm_environment" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_52]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_installer.f90"' + fpm = fpm//NL//'digest = 6893981694820313345' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_installer"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_environment", "fpm_filesystem", "fpm_installer" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_53]' + fpm = fpm//NL//'file-name = "test/fpm_test/main.f90"' + fpm = fpm//NL//'exe-name = "fpm-test"' + fpm = fpm//NL//'digest = -6659997723519103741' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "test_toml", "test_manifest", "test_os" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_54]' + fpm = fpm//NL//'file-name = "test/fpm_test/testsuite.f90"' + fpm = fpm//NL//'digest = 4708439108904007602' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "testsuite"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "fpm_error"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_55]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_toml.f90"' + fpm = fpm//NL//'digest = -4238391920328466228' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_toml"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_toml", "tomlf_constants", "fpm_compiler" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_56]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_package_dependencies.f90"' + fpm = fpm//NL//'digest = 1143008373292682612' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_package_dependencies"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "testsuite", "fpm_versioning", "jonquil" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_57]' + fpm = fpm//NL//'file-name = "test/fpm_test/test_module_dependencies.f90"' + fpm = fpm//NL//'digest = -8398823885747598218' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "test_module_dependencies"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_targets", "fpm" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_58]' + fpm = fpm//NL//'file-name = "test/cli_test/cli_test.f90"' + fpm = fpm//NL//'exe-name = "cli-test"' + fpm = fpm//NL//'digest = 7502982943646619950' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm", "fpm_cmd_new" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.fpm.sources.src_59]' + fpm = fpm//NL//'file-name = "test/new_test/new_test.f90"' + fpm = fpm//NL//'exe-name = "new-test"' + fpm = fpm//NL//'digest = 4683353150944180202' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' + fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_strings", "fpm_environment" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f]' + fpm = fpm//NL//'name = "toml-f"' + fpm = fpm//NL//'version = "0.8.0"' + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + fpm = fpm//NL//'[packages.toml-f.fortran]' + fpm = fpm//NL//'implicit-typing = false' + fpm = fpm//NL//'implicit-external = false' + fpm = fpm//NL//'source-form = "free"' + fpm = fpm//NL//'[packages.toml-f.sources]' + fpm = fpm//NL//'[packages.toml-f.sources.src_1]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf.f90"' + fpm = fpm//NL//'digest = -8299830903248890534' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_build", "tomlf_datetime" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_2]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/constants.f90"' + fpm = fpm//NL//'digest = 7170350792708576173' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_constants"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_3]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/version.f90"' + fpm = fpm//NL//'digest = 7297460108185920032' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_version"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_4]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure.f90"' + fpm = fpm//NL//'digest = -5586939372904264461' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_structure"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_structure_ordered_map" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_5]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/error.f90"' + fpm = fpm//NL//'digest = -6990387780017431402' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_error"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_constants"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_6]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/ser.f90"' + fpm = fpm//NL//'digest = 2173577414279434444' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_ser"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_utils" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_7]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de.f90"' + fpm = fpm//NL//'digest = 6984491308379570724' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_de"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_8]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/utils.f90"' + fpm = fpm//NL//'digest = -1654455727593730955' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_utils"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_utils_io" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_9]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/datetime.f90"' + fpm = fpm//NL//'digest = 360194003049506468' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_datetime"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_constants"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_10]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/diagnostic.f90"' + fpm = fpm//NL//'digest = -6145654881147673446' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_diagnostic"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_terminal"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_11]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type.f90"' + fpm = fpm//NL//'digest = 7822704506185839449' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_type"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_12]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build.f90"' + fpm = fpm//NL//'digest = 6734874397655167084' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_build"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_build_array", "tomlf_build_table" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_13]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/all.f90"' + fpm = fpm//NL//'digest = -3373616532185720889' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_all"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_build", "tomlf_type", "tomlf_utils", "tomlf_version" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_14]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/terminal.f90"' + fpm = fpm//NL//'digest = 6124874315911091908' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_terminal"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_utils"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_15]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/utils/sort.f90"' + fpm = fpm//NL//'digest = -7275638313901306893' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_utils_sort"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_type_value"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_16]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/utils/io.f90"' + fpm = fpm//NL//'digest = -4559681945420894782' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_utils_io"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_constants"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_17]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/keyval.f90"' + fpm = fpm//NL//'digest = 7305553188003635285' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_type_keyval"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_type_value" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_18]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/table.f90"' + fpm = fpm//NL//'digest = -1731470661964884986' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_type_table"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_structure" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_19]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/array.f90"' + fpm = fpm//NL//'digest = 5202963073293705116' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_type_array"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_error", "tomlf_type_value", "tomlf_structure" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_20]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/value.f90"' + fpm = fpm//NL//'digest = 988208496786453415' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_type_value"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_utils" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_21]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/context.f90"' + fpm = fpm//NL//'digest = -6236998766484611847' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_de_context"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_terminal" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_22]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/lexer.f90"' + fpm = fpm//NL//'digest = -5703883624156149303' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_de_lexer"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_error", "tomlf_utils" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_23]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/token.f90"' + fpm = fpm//NL//'digest = -6068697997670165243' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_de_token"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_24]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/parser.f90"' + fpm = fpm//NL//'digest = -3187016653233800622' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_de_parser"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_type" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_25]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/abc.f90"' + fpm = fpm//NL//'digest = -1146733275418683599' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_de_abc"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_de_token" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_26]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/merge.f90"' + fpm = fpm//NL//'digest = -8357953095488542628' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_build_merge"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_27]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/keyval.f90"' + fpm = fpm//NL//'digest = -4107572447442746790' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_build_keyval"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_error", "tomlf_type", "tomlf_utils" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_28]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/table.f90"' + fpm = fpm//NL//'digest = 3419266420890706227' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_build_table"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_build_keyval", "tomlf_constants", "tomlf_datetime", "tomlf_error", "tomlf_type" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_29]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/array.f90"' + fpm = fpm//NL//'digest = 5731959908631518546' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_build_array"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_build_keyval", "tomlf_error", "tomlf_type" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_30]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/path.f90"' + fpm = fpm//NL//'digest = 1001559863484583002' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_build_path"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_build_table", "tomlf_error", "tomlf_type" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_31]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/node.f90"' + fpm = fpm//NL//'digest = 4105605469572416054' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_structure_node"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = "tomlf_type_value"' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_32]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/array_list.f90"' + fpm = fpm//NL//'digest = 1707150725310470906' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_structure_array_list"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_33]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/ordered_map.f90"' + fpm = fpm//NL//'digest = 9194757273934069933' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_structure_ordered_map"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_34]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/map.f90"' + fpm = fpm//NL//'digest = 10697944851042277' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_structure_map"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.toml-f.sources.src_35]' + fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/list.f90"' + fpm = fpm//NL//'digest = 6018335058365199200' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "tomlf_structure_list"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.M_CLI2]' + fpm = fpm//NL//'name = "M_CLI2"' + fpm = fpm//NL//'version = "0.8.0"' + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + fpm = fpm//NL//'[packages.M_CLI2.fortran]' + fpm = fpm//NL//'implicit-typing = false' + fpm = fpm//NL//'implicit-external = false' + fpm = fpm//NL//'source-form = "free"' + fpm = fpm//NL//'[packages.M_CLI2.sources]' + fpm = fpm//NL//'[packages.M_CLI2.sources.src_1]' + fpm = fpm//NL//'file-name = "build/dependencies/M_CLI2/src/M_CLI2.F90"' + fpm = fpm//NL//'digest = -6169834068995303802' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "m_cli2"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.jonquil]' + fpm = fpm//NL//'name = "jonquil"' + fpm = fpm//NL//'version = "0.8.0"' + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + fpm = fpm//NL//'[packages.jonquil.fortran]' + fpm = fpm//NL//'implicit-typing = false' + fpm = fpm//NL//'implicit-external = false' + fpm = fpm//NL//'source-form = "free"' + fpm = fpm//NL//'[packages.jonquil.sources]' + fpm = fpm//NL//'[packages.jonquil.sources.src_1]' + fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil.f90"' + fpm = fpm//NL//'digest = 5552073973512025871' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "jonquil"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf", "tomlf_type", "jonquil_ser" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.jonquil.sources.src_2]' + fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/version.f90"' + fpm = fpm//NL//'digest = -2934903401983932826' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "jonquil_version"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.jonquil.sources.src_3]' + fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/ser.f90"' + fpm = fpm//NL//'digest = 2690773570566028936' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "jonquil_ser"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_error", "tomlf_utils" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.jonquil.sources.src_4]' + fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/lexer.f90"' + fpm = fpm//NL//'digest = 4057038173684122483' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "jonquil_lexer"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_utils" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'[packages.jonquil.sources.src_5]' + fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/parser.f90"' + fpm = fpm//NL//'digest = -2426842130572494815' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' + fpm = fpm//NL//'modules-provided = "jonquil_parser"' + fpm = fpm//NL//'parent-modules = [ ]' + fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_de_context" ]' + fpm = fpm//NL//'include-dependencies = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + + ! Write + open(newunit=iunit,form='formatted',status='scratch') + + !> Dump to scratch file + write(iunit,*) fpm + + !> Load from scratch file + rewind(iunit) + call toml_load(table, iunit) + + close(iunit) + + end subroutine fpm_081_table + + subroutine fpm_model_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + type(fpm_model_t) :: model + type(toml_table), allocatable :: table + + call model%test_serialization('fpm_model_t: default object', error) + if (allocated(error)) return + + !> Now init form fpm 0.8.1 table + call fpm_081_table(table) + + call model%load(table, error) + if (allocated(error)) then + call fatal_error(error, 'fpm_model_t: cannot load model from fpm v0.8.1 TOML') + return + end if + + call model%test_serialization('fpm_model_t: fpm v0.8.1 model test', error) + + end subroutine fpm_model_roundtrip + end module test_toml From 679327a777b553470e7f1d26e0ba64d90c972f4b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 09:34:32 +0200 Subject: [PATCH 291/799] add failing tests --- src/fpm/dependency.f90 | 49 +++++- src/fpm_model.f90 | 8 +- test/fpm_test/test_toml.f90 | 338 +++++++++++++++++++++++++++++++++--- 3 files changed, 366 insertions(+), 29 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 45c9cc44ce..c78a758dd2 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1350,6 +1350,7 @@ subroutine node_load_from_toml(self, table, error) !> Local variables character(len=:), allocatable :: version + integer :: ierr call destroy_dependency_node(self) @@ -1357,9 +1358,30 @@ subroutine node_load_from_toml(self, table, error) call self%dependency_config_t%load_from_toml(table, error) if (allocated(error)) return - call get_value(table, "done", self%done) - call get_value(table, "update", self%update) - call get_value(table, "cached", self%cached) + call get_value(table, "done", self%done, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot read done flag in TOML table') + return + end if + + call get_value(table, "update", self%update, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot read update flag in TOML table') + return + end if + + call get_value(table, "cached", self%cached, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot read cached flag in TOML table') + return + end if + + call get_value(table, "done", self%done, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_node_t: cannot read done flag in TOML table') + return + end if + call get_value(table, "proj-dir", self%proj_dir) call get_value(table, "revision", self%revision) @@ -1528,9 +1550,24 @@ subroutine tree_load_from_toml(self, table, error) call table%get_keys(keys) - call get_value(table, "unit", self%unit) - call get_value(table, "verbosity", self%verbosity) - call get_value(table, "ndep", self%ndep) + call get_value(table, "unit", self%unit, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set in TOML table') + return + end if + + call get_value(table, "verbosity", self%verbosity, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set in TOML table') + return + end if + + call get_value(table, "ndep", self%ndep, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'dependency_tree_t: cannot set in TOML table') + return + end if + call get_value(table, "dep-dir", self%dep_dir) call get_value(table, "cache", self%cache) diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index f0593b16b1..18110571a7 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -569,10 +569,16 @@ subroutine srcfile_load_from_toml(self, table, error) type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: flag + integer :: ierr call get_value(table, "file-name", self%file_name) call get_value(table, "exe-name", self%exe_name) - call get_value(table, "digest", self%digest) + + call get_value(table, "digest", self%digest, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'srcfile_t: cannot set digest in TOML table') + return + end if ! unit_scope and unit_type are saved as strings so the output is independent ! of the internal representation diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index d7afad507a..0162472b42 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -9,7 +9,8 @@ module test_toml use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split - use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t + use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & + & srcfile_t use fpm_compiler, only: archiver_t, compiler_t, id_gcc implicit none @@ -17,6 +18,7 @@ module test_toml public :: collect_toml + character, parameter :: NL = new_line('a') contains @@ -28,19 +30,30 @@ subroutine collect_toml(testsuite) type(unittest_t), allocatable, intent(out) :: testsuite(:) testsuite = [ & - & new_unittest("valid-toml", test_valid_toml), & - & new_unittest("invalid-toml", test_invalid_toml, should_fail=.true.), & - & new_unittest("missing-file", test_missing_file, should_fail=.true.), & - & new_unittest("serialize-git-target", git_target_roundtrip), & - & new_unittest("serialize-dependency-config", dependency_config_roundtrip), & - & new_unittest("serialize-dependency-node", dependency_node_roundtrip), & - & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & - & new_unittest("serialize-string-array", string_array_roundtrip), & - & new_unittest("serialize-fortran-features", fft_roundtrip), & - & new_unittest("serialize-package", package_roundtrip), & - & new_unittest("serialize-archiver", ar_roundtrip), & - & new_unittest("serialize-compiler", compiler_roundtrip), & - & new_unittest("serialize-model", fpm_model_roundtrip)] + & new_unittest("valid-toml", test_valid_toml), & + & new_unittest("invalid-toml", test_invalid_toml, should_fail=.true.), & + & new_unittest("missing-file", test_missing_file, should_fail=.true.), & + & new_unittest("serialize-git-target", git_target_roundtrip), & + & new_unittest("serialize-git-invalid", git_target_invalid, should_fail=.true.), & + & new_unittest("serialize-dependency-config", dependency_config_roundtrip), & + & new_unittest("serialize-dependency-node", dependency_node_roundtrip), & + & new_unittest("serialize-dependency-invalid", dependency_node_invalid, should_fail=.true.), & + & new_unittest("serialize-dependency-invalid2", dependency_node_invalid_2, should_fail=.true.), & + & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & + & new_unittest("serialize-dependency-tree-invalid", dependency_tree_invalid, should_fail=.true.), & + & new_unittest("serialize-dependency-tree-invalid2", dependency_tree_invalid2, should_fail=.true.), & + & new_unittest("serialize-string-array", string_array_roundtrip), & + & new_unittest("serialize-fortran-features", fft_roundtrip), & + & new_unittest("serialize-fortran-invalid", fft_invalid, should_fail=.true.), & + & new_unittest("serialize-package", package_roundtrip), & + & new_unittest("serialize-package-invalid", package_invalid, should_fail=.true.), & + & new_unittest("serialize-srcfile-invalid", source_invalid, should_fail=.true.), & + & new_unittest("serialize-archiver", ar_roundtrip), & + & new_unittest("serialize-archiver-invalid", ar_invalid, should_fail=.true.), & + & new_unittest("serialize-compiler", compiler_roundtrip), & + & new_unittest("serialize-compiler-invalid", compiler_invalid, should_fail=.true.), & + & new_unittest("serialize-model", fpm_model_roundtrip), & + & new_unittest("serialize-model-invalid", fpm_model_invalid, should_fail=.true.)] end subroutine collect_toml @@ -162,6 +175,26 @@ subroutine git_target_roundtrip(error) end subroutine git_target_roundtrip + !> Test invalid git_target_t serialization + subroutine git_target_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(git_target_t) :: git + type(toml_table), allocatable :: table + + character(*), parameter :: toml = 'descriptor = ""'//NL//& ! invalid descriptor ID + 'url = "https://github.com/toml-f/toml-f"'//NL//& + 'object = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f"' + + + call string_to_toml(toml, table) + + call git%load(table, error) + + end subroutine git_target_invalid + !> Test git_target_t serialization subroutine dependency_config_roundtrip(error) @@ -286,6 +319,51 @@ subroutine dependency_node_roundtrip(error) end subroutine dependency_node_roundtrip + !> Test loading invalid dependency node + subroutine dependency_node_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: dep + type(toml_table), allocatable :: table + + character(*), parameter :: toml = 'name = "jonquil" '//NL//& + & 'version = "h0.2.0"'//NL//& ! invalid version + & 'proj-dir = "build/dependencies/jonquil"'//NL//& + & 'revision = "05d30818bb12fb877226ce284b9a3a41b971a889"'//NL//& + & 'done = true'//NL//& + & 'update = false'//NL//& + & 'cached = true' + + call string_to_toml(toml, table) + + call dep%load(table, error) + + end subroutine dependency_node_invalid + + !> Test loading invalid dependency node + subroutine dependency_node_invalid_2(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(dependency_node_t) :: dep + type(toml_table), allocatable :: table + + character(*), parameter :: toml = 'name = "jonquil" '//NL//& + & 'version = "0.2.0"'//NL//& + & 'proj-dir = "build/dependencies/jonquil"'//NL//& + & 'revision = "05d30818bb12fb877226ce284b9a3a41b971a889"'//NL//& + & 'done = 123'//NL//& ! not a boolean + & 'update = false'//NL//& + & 'cached = true' + + call string_to_toml(toml, table) + call dep%load(table, error) + + end subroutine dependency_node_invalid_2 + !> Test dependency_tree_t serialization subroutine dependency_tree_roundtrip(error) @@ -349,12 +427,73 @@ subroutine dependency_tree_roundtrip(error) call deps%test_serialization("no deps dir", error) if (allocated(error)) return - - 1 format('removed ',i0,' dependencies') end subroutine dependency_tree_roundtrip + !> Test invalid dependency tree loading + subroutine dependency_tree_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), allocatable :: table + type(dependency_tree_t) :: dep + + character(len=*), parameter :: toml = & + & 'unit = 6 '//NL//& + & 'verbosity = true'//NL//& ! not a number + & 'dep-dir = "build/dependencies"'//NL//& + & 'ndep = 3'//NL//& ! consistency is not checked: + & '[dependencies]'//NL//& + & '[dependencies.dep1]'//NL//& + & 'name = "dep1"'//NL//& + & 'path = "fpm-tmp1-dir"'//NL//& + & 'proj-dir = "fpm-tmp1-dir"'//NL//& + & 'done = false'//NL//& + & 'update = false'//NL//& + & 'cached = false' + + call string_to_toml(toml, table) + call dep%load(table, error) + + end subroutine dependency_tree_invalid + + !> Test invalid dependency tree loading + subroutine dependency_tree_invalid2(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), allocatable :: table + type(dependency_tree_t) :: dep + + character(len=*), parameter :: toml = & + & 'unit = "" '//NL//& ! not provided + & 'verbosity = 1'//NL//& + & 'dep-dir = "build/dependencies"'//NL//& + & 'ndep = 3'//NL//& ! consistency is not checked: + & '[dependencies.M_CLI2]'//NL//& + & 'name = "M_CLI2"'//NL//& + & 'path = "~/./some/dummy/path"'//NL//& + & 'namespace = "urbanjost"'//NL//& + & 'requested_version = "3.2.0"'//NL//& + & 'version = "4.53.2"'//NL//& + & 'proj-dir = "~/./"'//NL//& + & 'revision = "7264878cdb1baff7323cc48596d829ccfe7751b8"'//NL//& + & 'done = false'//NL//& + & 'update = true'//NL//& + & 'cached = true'//NL//& + & '[dependencies.M_CLI2.git]'//NL//& + & 'descriptor = "revision"'//NL//& + & 'url = "https://github.com/urbanjost/M_CLI2.git"'//NL//& + & 'object = "7264878cdb1baff7323cc48596d829ccfe7751b8"' + + call string_to_toml(toml, table) + call dep%load(table, error) + + end subroutine dependency_tree_invalid2 + !> Test serialization/deserialization of a string array subroutine string_array_roundtrip(error) @@ -463,6 +602,26 @@ subroutine fft_roundtrip(error) end subroutine fft_roundtrip + !> Test deserialization of an invalid fortran-features structure + subroutine fft_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(fortran_features_t) :: fortran + type(toml_table), allocatable :: table + + character(len=*), parameter :: toml = 'implicit-typing = false '//NL//& + & 'implicit-external = 0 '//NL//& ! not a boolean + & 'source-form = "free" ' + + call string_to_toml(toml, table) + + !> Default object + call fortran%load(table,error) + + end subroutine fft_invalid + !> Test serialization/deserialization of a package_t structure subroutine package_roundtrip(error) @@ -545,6 +704,55 @@ subroutine package_roundtrip(error) end subroutine package_roundtrip + !> Test deserialization of an invalid package TOML file + subroutine package_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=*), parameter :: toml = & + & 'name = "toml-f" '//NL//& + & 'version = "0.8.0" '//NL//& + & 'module-naming = "prefix" '//NL//& ! this should be boolean + & 'module-prefix = "" ' + + type(package_t) :: pkg + type(toml_table), allocatable :: table + + call string_to_toml(toml, table) + + !> Default object + call pkg%load(table,error) + + end subroutine package_invalid + + !> Test deserialization of an invalid source file + subroutine source_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=*), parameter :: toml = & + & 'file-name = "build/dependencies/toml-f/src/tomlf.f90" '//NL//& + & 'digest = "abcde" '//NL//& ! not a number + & 'unit-scope = "FPM_SCOPE_MODULE" '//NL//& + & 'unit-type = "FPM_UNIT_MODULE" '//NL//& + & 'modules-provided = "tomlf" '//NL//& + & 'parent-modules = [ ] '//NL//& + & 'modules-used = [ "tomlf_build", "tomlf_datetime" ] '//NL//& + & 'include-dependencies = [ ] '//NL//& + & 'link-libraries = [ ] ' + + type(srcfile_t) :: src + type(toml_table), allocatable :: table + + call string_to_toml(toml, table) + + !> Default object + call src%load(table,error) + + end subroutine source_invalid + !> Test serialization/deserialization of an archiver_t structure subroutine ar_roundtrip(error) @@ -567,6 +775,27 @@ subroutine ar_roundtrip(error) end subroutine ar_roundtrip + !> Test deserialization of an invalid archiver_t structure + subroutine ar_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=*), parameter :: toml = & + & 'ar = "ar -rs " '//NL//& + & 'use-response-file = false '//NL//& + & 'echo = 123 '//NL//& ! not a boolean + & 'verbose = false ' + + type(archiver_t) :: ar + type(toml_table), allocatable :: table + + call string_to_toml(toml, table) + + !> Default object + call ar%load(table,error) + + end subroutine ar_invalid !> Test serialization/deserialization of a compiler_t structure subroutine compiler_roundtrip(error) @@ -587,12 +816,34 @@ subroutine compiler_roundtrip(error) compiler%cxx = "g++ -O3 -std=c++11" compiler%echo = .false. - call compiler%dump('compiler.toml',error) - call compiler%test_serialization('compiler_t: gcc',error) end subroutine compiler_roundtrip + !> Test deserialization of an invalid compiler_t TOML structure + subroutine compiler_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(len=*), parameter :: toml = & + & 'id = "gfortran" '//NL//& ! not an integer identifier + & 'fc = "gfortran" '//NL//& + & 'cc = "gcc" '//NL//& + & 'cxx = "g++" '//NL//& + & 'echo = false '//NL//& + & 'verbose = false ' + + type(compiler_t) :: cc + type(toml_table), allocatable :: table + + call string_to_toml(toml, table) + + !> Default object + call cc%load(table,error) + + end subroutine compiler_invalid + !> Get a simplified TOML representation of the fpm v0.8.1 model subroutine fpm_081_table(table) @@ -600,7 +851,6 @@ subroutine fpm_081_table(table) type(toml_table), allocatable, intent(out) :: table !> simplified TOML representation of the fpm v0.8.1 model - character, parameter :: NL = new_line('a') character(len=:), allocatable :: fpm integer :: iunit @@ -1812,11 +2062,26 @@ subroutine fpm_081_table(table) fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' + call string_to_toml(fpm, table) + + end subroutine fpm_081_table + + !> Convert a character string to a TOML table + subroutine string_to_toml(string, table) + + !> The input TOML as a string + character(*), intent(in) :: string + + !> The TOML table + type(toml_table), allocatable, intent(out) :: table + + integer :: iunit + ! Write - open(newunit=iunit,form='formatted',status='scratch') + open(newunit=iunit,form='formatted',status='scratch',action='readwrite') !> Dump to scratch file - write(iunit,*) fpm + write(iunit,*) string !> Load from scratch file rewind(iunit) @@ -1824,7 +2089,7 @@ subroutine fpm_081_table(table) close(iunit) - end subroutine fpm_081_table + end subroutine string_to_toml subroutine fpm_model_roundtrip(error) @@ -1849,4 +2114,33 @@ subroutine fpm_model_roundtrip(error) end subroutine fpm_model_roundtrip + + subroutine fpm_model_invalid(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(fpm_model_t) :: model + type(toml_table), allocatable :: table + character(len=:), allocatable :: fpm + + allocate(character(len=0) :: fpm) + fpm = fpm//NL//'package-name = "fpm"' + fpm = fpm//NL//'fortran-flags = " -Wall -Wextra -fPIC -fmax-errors=1 -g "' + fpm = fpm//NL//'c-flags = ""' + fpm = fpm//NL//'cxx-flags = ""' + fpm = fpm//NL//'link-flags = ""' + fpm = fpm//NL//'build-prefix = "build/gfortran"' + fpm = fpm//NL//'include-dirs = [ ]' + fpm = fpm//NL//'link-libraries = [ ]' + fpm = fpm//NL//'external-modules = "" ' + fpm = fpm//NL//'include-tests = "my_test"' ! not a boolean + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + + call string_to_toml(fpm, table) + call model%load(table,error) + + end subroutine fpm_model_invalid + end module test_toml From ae56c6522b318211f721b32bb1194641e7b13ca0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 09:37:20 +0200 Subject: [PATCH 292/799] shorten fpm 0.8.1 table --- test/fpm_test/test_toml.f90 | 1088 ++--------------------------------- 1 file changed, 45 insertions(+), 1043 deletions(-) diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 0162472b42..ed54db07a5 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -844,7 +844,7 @@ subroutine compiler_invalid(error) end subroutine compiler_invalid - !> Get a simplified TOML representation of the fpm v0.8.1 model + !> Get a shortened TOML representation of the fpm v0.8.1 model subroutine fpm_081_table(table) !> TOML representation of the fpm v0.8.1 model @@ -907,707 +907,69 @@ subroutine fpm_081_table(table) fpm = fpm//NL//'descriptor = "revision"' fpm = fpm//NL//'url = "https://github.com/toml-f/toml-f"' fpm = fpm//NL//'object = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f"' - fpm = fpm//NL//'[deps.dependencies.M_CLI2]' - fpm = fpm//NL//'name = "M_CLI2"' - fpm = fpm//NL//'version = "1.0.0"' - fpm = fpm//NL//'proj-dir = "build/dependencies/M_CLI2"' - fpm = fpm//NL//'revision = "7264878cdb1baff7323cc48596d829ccfe7751b8"' - fpm = fpm//NL//'done = true' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = true' - fpm = fpm//NL//'[deps.dependencies.M_CLI2.git]' - fpm = fpm//NL//'descriptor = "revision"' - fpm = fpm//NL//'url = "https://github.com/urbanjost/M_CLI2.git"' - fpm = fpm//NL//'object = "7264878cdb1baff7323cc48596d829ccfe7751b8"' - fpm = fpm//NL//'[deps.dependencies.jonquil]' - fpm = fpm//NL//'name = "jonquil"' - fpm = fpm//NL//'version = "0.2.0"' - fpm = fpm//NL//'proj-dir = "build/dependencies/jonquil"' - fpm = fpm//NL//'revision = "05d30818bb12fb877226ce284b9a3a41b971a889"' - fpm = fpm//NL//'done = true' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = true' - fpm = fpm//NL//'[deps.dependencies.jonquil.git]' - fpm = fpm//NL//'descriptor = "revision"' - fpm = fpm//NL//'url = "https://github.com/toml-f/jonquil"' - fpm = fpm//NL//'object = "05d30818bb12fb877226ce284b9a3a41b971a889"' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_5]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_6]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_7]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_8]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_9]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_10]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_11]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_12]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_13]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_14]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_15]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_16]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_17]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_18]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_19]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_20]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_21]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_22]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_23]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_24]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_25]' - fpm = fpm//NL//'done = false' - fpm = fpm//NL//'update = false' - fpm = fpm//NL//'cached = false' - fpm = fpm//NL//'[packages]' - fpm = fpm//NL//'[packages.fpm]' - fpm = fpm//NL//'name = "fpm"' - fpm = fpm//NL//'version = "0.8.0"' - fpm = fpm//NL//'module-naming = false' - fpm = fpm//NL//'module-prefix = ""' - fpm = fpm//NL//'[packages.fpm.fortran]' - fpm = fpm//NL//'implicit-typing = false' - fpm = fpm//NL//'implicit-external = false' - fpm = fpm//NL//'source-form = "free"' - fpm = fpm//NL//'[packages.fpm.sources]' - fpm = fpm//NL//'[packages.fpm.sources.src_1]' - fpm = fpm//NL//'file-name = "././src/fpm.f90"' - fpm = fpm//NL//'digest = 4322290725857190613' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_strings", "fpm_backend", "fpm_compiler", "fpm_error" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_2]' - fpm = fpm//NL//'file-name = "././src/fpm_backend.F90"' - fpm = fpm//NL//'digest = -3210121688944515946' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_backend"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_backend_output" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_3]' - fpm = fpm//NL//'file-name = "././src/fpm_environment.f90"' - fpm = fpm//NL//'digest = 2235607720245152632' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_environment"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "fpm_error"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_4]' - fpm = fpm//NL//'file-name = "././src/fpm_model.f90"' - fpm = fpm//NL//'digest = -6774177234665080583' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_model"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_compiler", "fpm_dependency" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_5]' - fpm = fpm//NL//'file-name = "././src/filesystem_utilities.c"' - fpm = fpm//NL//'digest = 4957633104775755438' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_6]' - fpm = fpm//NL//'file-name = "././src/fpm_filesystem.F90"' - fpm = fpm//NL//'digest = 1871084827152368652' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_filesystem"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_environment" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_7]' - fpm = fpm//NL//'file-name = "././src/fpm_strings.f90"' - fpm = fpm//NL//'digest = 7038915013685504829' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_strings"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_8]' - fpm = fpm//NL//'file-name = "././src/fpm_settings.f90"' - fpm = fpm//NL//'digest = -885425387141891996' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_settings"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_environment", "fpm_error" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_9]' - fpm = fpm//NL//'file-name = "././src/fpm_os.c"' - fpm = fpm//NL//'digest = -4523865409175594663' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_10]' - fpm = fpm//NL//'file-name = "././src/fpm_backend_console.f90"' - fpm = fpm//NL//'digest = 1732983699585955966' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_backend_console"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_11]' - fpm = fpm//NL//'file-name = "././src/fpm_source_parsing.f90"' - fpm = fpm//NL//'digest = 6098986130375861226' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_source_parsing"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_model", "fpm_filesystem" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_12]' - fpm = fpm//NL//'file-name = "././src/fpm_os.F90"' - fpm = fpm//NL//'digest = -4743856136050054640' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_os"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_environment", "fpm_error" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_13]' - fpm = fpm//NL//'file-name = "././src/fpm_compiler.F90"' - fpm = fpm//NL//'digest = -2442073797366752057' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_compiler"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_filesystem", "fpm_strings" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_14]' - fpm = fpm//NL//'file-name = "././src/fpm_command_line.f90"' - fpm = fpm//NL//'digest = 7180707928326338392' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_command_line"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_environment", "m_cli2", "m_cli2", "fpm_strings" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_15]' - fpm = fpm//NL//'file-name = "././src/fpm_backend_output.f90"' - fpm = fpm//NL//'digest = 7154367044486334558' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_backend_output"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_targets", "fpm_backend_console" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_16]' - fpm = fpm//NL//'file-name = "././src/fpm_targets.f90"' - fpm = fpm//NL//'digest = -8234965779941208361' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_targets"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_model", "fpm_compiler", "fpm_environment" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_17]' - fpm = fpm//NL//'file-name = "././src/fpm_sources.f90"' - fpm = fpm//NL//'digest = 3391120653956350167' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_sources"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_model", "fpm_filesystem", "fpm_strings" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_18]' - fpm = fpm//NL//'file-name = "././src/ptycheck/iscygpty.c"' - fpm = fpm//NL//'digest = -4887164695298162637' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = "iscygpty.h"' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_19]' - fpm = fpm//NL//'file-name = "././src/ptycheck/isatty.c"' - fpm = fpm//NL//'digest = 6664536934601490990' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = "iscygpty.h"' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_20]' - fpm = fpm//NL//'file-name = "././src/ptycheck/iscygpty.h"' - fpm = fpm//NL//'digest = -3550201113101300999' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_CHEADER"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_21]' - fpm = fpm//NL//'file-name = "././src/fpm/downloader.f90"' - fpm = fpm//NL//'digest = 620358568720613499' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_SUBPROGRAM"' - fpm = fpm//NL//'modules-provided = "fpm_downloader"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_versioning", "jonquil" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_22]' - fpm = fpm//NL//'file-name = "././src/fpm/error.f90"' - fpm = fpm//NL//'digest = 7324399436715753500' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_error"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "fpm_strings"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_23]' - fpm = fpm//NL//'file-name = "././src/fpm/toml.f90"' - fpm = fpm//NL//'digest = 2411620725015864401' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_toml"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "tomlf" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_24]' - fpm = fpm//NL//'file-name = "././src/fpm/installer.f90"' - fpm = fpm//NL//'digest = 581769321360482292' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_installer"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_error", "fpm_filesystem" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_25]' - fpm = fpm//NL//'file-name = "././src/fpm/versioning.f90"' - fpm = fpm//NL//'digest = -1370610786727991294' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_versioning"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "fpm_error"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_26]' - fpm = fpm//NL//'file-name = "././src/fpm/git.f90"' - fpm = fpm//NL//'digest = -7368368636549243157' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_git"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_27]' - fpm = fpm//NL//'file-name = "././src/fpm/dependency.f90"' - fpm = fpm//NL//'digest = -2836785909441977019' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_dependency"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_environment", "fpm_error", "fpm_filesystem"]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_28]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest.f90"' - fpm = fpm//NL//'digest = -1346850924839827718' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_manifest_build", "fpm_manifest_example" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_29]' - fpm = fpm//NL//'file-name = "././src/fpm/cmd/new.f90"' - fpm = fpm//NL//'digest = 697853208011446608' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_cmd_new"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm_environment" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_30]' - fpm = fpm//NL//'file-name = "././src/fpm/cmd/update.f90"' - fpm = fpm//NL//'digest = -8232305547308400988' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_cmd_update"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm_manifest" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_31]' - fpm = fpm//NL//'file-name = "././src/fpm/cmd/install.f90"' - fpm = fpm//NL//'digest = -6707501025391219376' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_cmd_install"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm", "fpm_backend", "fpm_command_line" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_32]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/test.f90"' - fpm = fpm//NL//'digest = 1399197227023080626' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_test"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_manifest_dependency", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_33]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/profiles.f90"' - fpm = fpm//NL//'digest = -7975317648924650587' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_profile"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_toml", "fpm_strings", "fpm_filesystem" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_34]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/example.f90"' - fpm = fpm//NL//'digest = 2220193652669081694' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_example"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_manifest_dependency", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_35]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/executable.f90"' - fpm = fpm//NL//'digest = 2826537585451151940' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_executable"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_manifest_dependency", "fpm_error", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_36]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/install.f90"' - fpm = fpm//NL//'digest = 6941308343630725905' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_install"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_37]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/package.f90"' - fpm = fpm//NL//'digest = 4046915203104200691' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_package"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_manifest_build", "fpm_manifest_dependency", ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_38]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/preprocess.f90"' - fpm = fpm//NL//'digest = 4463864760686846214' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_mainfest_preprocess"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_39]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/build.f90"' - fpm = fpm//NL//'digest = 7486174362460284832' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_build"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_40]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/dependency.f90"' - fpm = fpm//NL//'digest = -6006235286439662663' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_dependency"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_git", "fpm_versioning" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_41]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/library.f90"' - fpm = fpm//NL//'digest = -1698783511442136567' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_library"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_strings", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_42]' - fpm = fpm//NL//'file-name = "././src/fpm/manifest/fortran.f90"' - fpm = fpm//NL//'digest = -6768952943164424742' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "fpm_manifest_fortran"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_toml" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_43]' - fpm = fpm//NL//'file-name = "app/main.f90"' - fpm = fpm//NL//'exe-name = "fpm"' - fpm = fpm//NL//'digest = 7759460120440225004' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_APP"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm_os" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_44]' - fpm = fpm//NL//'file-name = "test/help_test/help_test.f90"' - fpm = fpm//NL//'exe-name = "help-test"' - fpm = fpm//NL//'digest = -7601948172740854190' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' - fpm = fpm//NL//'modules-provided = [ ]' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "fpm_environment" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_45]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_os.f90"' - fpm = fpm//NL//'digest = 718441623146001654' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_os"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_filesystem", "fpm_environment", "fpm_os" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_46]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_source_parsing.f90"' - fpm = fpm//NL//'digest = 5852386252678959798' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_SUBPROGRAM"' - fpm = fpm//NL//'modules-provided = "test_source_parsing"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_filesystem", "fpm_strings" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_47]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_settings.f90"' - fpm = fpm//NL//'digest = -3541669032396077479' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_settings"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_settings", "fpm_filesystem", "fpm_os" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_48]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_backend.f90"' - fpm = fpm//NL//'digest = 2723265999281936523' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_backend"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "test_module_dependencies", "fpm_backend" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_49]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_versioning.f90"' - fpm = fpm//NL//'digest = 7879213895027593947' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_versioning"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_versioning" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_50]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_manifest.f90"' - fpm = fpm//NL//'digest = -5417606542127631442' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_manifest"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "testsuite", "fpm_manifest" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_51]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_filesystem.f90"' - fpm = fpm//NL//'digest = -3128825714354096496' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'[deps.dependencies.UNNAMED_DEPENDENCY_5]' + fpm = fpm//NL//'done = false' + fpm = fpm//NL//'update = false' + fpm = fpm//NL//'cached = false' + fpm = fpm//NL//'[packages]' + fpm = fpm//NL//'[packages.fpm]' + fpm = fpm//NL//'name = "fpm"' + fpm = fpm//NL//'version = "0.8.0"' + fpm = fpm//NL//'module-naming = false' + fpm = fpm//NL//'module-prefix = ""' + fpm = fpm//NL//'[packages.fpm.fortran]' + fpm = fpm//NL//'implicit-typing = false' + fpm = fpm//NL//'implicit-external = false' + fpm = fpm//NL//'source-form = "free"' + fpm = fpm//NL//'[packages.fpm.sources]' + fpm = fpm//NL//'[packages.fpm.sources.src_1]' + fpm = fpm//NL//'file-name = "././src/fpm.f90"' + fpm = fpm//NL//'digest = 4322290725857190613' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_filesystem"' + fpm = fpm//NL//'modules-provided = "fpm"' fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_filesystem", "fpm_environment" ]' + fpm = fpm//NL//'modules-used = [ "fpm_strings", "fpm_backend", "fpm_compiler", "fpm_error" ]' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_52]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_installer.f90"' - fpm = fpm//NL//'digest = 6893981694820313345' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'[packages.fpm.sources.src_2]' + fpm = fpm//NL//'file-name = "././src/fpm_backend.F90"' + fpm = fpm//NL//'digest = -3210121688944515946' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_installer"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_environment", "fpm_filesystem", "fpm_installer" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_53]' - fpm = fpm//NL//'file-name = "test/fpm_test/main.f90"' - fpm = fpm//NL//'exe-name = "fpm-test"' - fpm = fpm//NL//'digest = -6659997723519103741' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' - fpm = fpm//NL//'modules-provided = [ ]' + fpm = fpm//NL//'modules-provided = "fpm_backend"' fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "test_toml", "test_manifest", "test_os" ]' + fpm = fpm//NL//'modules-used = [ "fpm_error", "fpm_filesystem", "fpm_backend_output" ]' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_54]' - fpm = fpm//NL//'file-name = "test/fpm_test/testsuite.f90"' - fpm = fpm//NL//'digest = 4708439108904007602' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'[packages.fpm.sources.src_3]' + fpm = fpm//NL//'file-name = "././src/fpm_environment.f90"' + fpm = fpm//NL//'digest = 2235607720245152632' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "testsuite"' + fpm = fpm//NL//'modules-provided = "fpm_environment"' fpm = fpm//NL//'parent-modules = [ ]' fpm = fpm//NL//'modules-used = "fpm_error"' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_55]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_toml.f90"' - fpm = fpm//NL//'digest = -4238391920328466228' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_toml"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_toml", "tomlf_constants", "fpm_compiler" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_56]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_package_dependencies.f90"' - fpm = fpm//NL//'digest = 1143008373292682612' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_package_dependencies"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_filesystem", "testsuite", "fpm_versioning", "jonquil" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_57]' - fpm = fpm//NL//'file-name = "test/fpm_test/test_module_dependencies.f90"' - fpm = fpm//NL//'digest = -8398823885747598218' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' + fpm = fpm//NL//'[packages.fpm.sources.src_4]' + fpm = fpm//NL//'file-name = "././src/fpm_model.f90"' + fpm = fpm//NL//'digest = -6774177234665080583' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "test_module_dependencies"' + fpm = fpm//NL//'modules-provided = "fpm_model"' fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "testsuite", "fpm_targets", "fpm" ]' + fpm = fpm//NL//'modules-used = [ "fpm_compiler", "fpm_dependency" ]' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.fpm.sources.src_58]' - fpm = fpm//NL//'file-name = "test/cli_test/cli_test.f90"' - fpm = fpm//NL//'exe-name = "cli-test"' - fpm = fpm//NL//'digest = 7502982943646619950' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_TEST"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_PROGRAM"' + fpm = fpm//NL//'[packages.fpm.sources.src_5]' + fpm = fpm//NL//'file-name = "././src/filesystem_utilities.c"' + fpm = fpm//NL//'digest = 4957633104775755438' + fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' + fpm = fpm//NL//'unit-type = "FPM_UNIT_CSOURCE"' fpm = fpm//NL//'modules-provided = [ ]' fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "fpm_command_line", "fpm", "fpm_cmd_new" ]' + fpm = fpm//NL//'modules-used = [ ]' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' fpm = fpm//NL//'[packages.fpm.sources.src_59]' @@ -1651,336 +1013,6 @@ subroutine fpm_081_table(table) fpm = fpm//NL//'modules-used = [ ]' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_3]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/version.f90"' - fpm = fpm//NL//'digest = 7297460108185920032' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_version"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_4]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure.f90"' - fpm = fpm//NL//'digest = -5586939372904264461' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_structure"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_structure_ordered_map" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_5]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/error.f90"' - fpm = fpm//NL//'digest = -6990387780017431402' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_error"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_constants"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_6]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/ser.f90"' - fpm = fpm//NL//'digest = 2173577414279434444' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_ser"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_utils" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_7]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de.f90"' - fpm = fpm//NL//'digest = 6984491308379570724' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_de"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_8]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/utils.f90"' - fpm = fpm//NL//'digest = -1654455727593730955' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_utils"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_utils_io" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_9]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/datetime.f90"' - fpm = fpm//NL//'digest = 360194003049506468' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_datetime"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_constants"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_10]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/diagnostic.f90"' - fpm = fpm//NL//'digest = -6145654881147673446' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_diagnostic"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_terminal"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_11]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type.f90"' - fpm = fpm//NL//'digest = 7822704506185839449' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_type"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_12]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build.f90"' - fpm = fpm//NL//'digest = 6734874397655167084' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_build"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_build_array", "tomlf_build_table" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_13]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/all.f90"' - fpm = fpm//NL//'digest = -3373616532185720889' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_all"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_build", "tomlf_type", "tomlf_utils", "tomlf_version" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_14]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/terminal.f90"' - fpm = fpm//NL//'digest = 6124874315911091908' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_terminal"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_utils"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_15]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/utils/sort.f90"' - fpm = fpm//NL//'digest = -7275638313901306893' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_utils_sort"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_type_value"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_16]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/utils/io.f90"' - fpm = fpm//NL//'digest = -4559681945420894782' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_utils_io"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_constants"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_17]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/keyval.f90"' - fpm = fpm//NL//'digest = 7305553188003635285' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_type_keyval"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_type_value" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_18]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/table.f90"' - fpm = fpm//NL//'digest = -1731470661964884986' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_type_table"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_structure" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_19]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/array.f90"' - fpm = fpm//NL//'digest = 5202963073293705116' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_type_array"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_error", "tomlf_type_value", "tomlf_structure" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_20]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/type/value.f90"' - fpm = fpm//NL//'digest = 988208496786453415' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_type_value"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_utils" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_21]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/context.f90"' - fpm = fpm//NL//'digest = -6236998766484611847' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_de_context"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_terminal" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_22]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/lexer.f90"' - fpm = fpm//NL//'digest = -5703883624156149303' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_de_lexer"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_error", "tomlf_utils" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_23]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/token.f90"' - fpm = fpm//NL//'digest = -6068697997670165243' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_de_token"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_24]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/parser.f90"' - fpm = fpm//NL//'digest = -3187016653233800622' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_de_parser"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_type" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_25]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/de/abc.f90"' - fpm = fpm//NL//'digest = -1146733275418683599' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_de_abc"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_de_token" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_26]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/merge.f90"' - fpm = fpm//NL//'digest = -8357953095488542628' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_build_merge"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_27]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/keyval.f90"' - fpm = fpm//NL//'digest = -4107572447442746790' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_build_keyval"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_error", "tomlf_type", "tomlf_utils" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_28]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/table.f90"' - fpm = fpm//NL//'digest = 3419266420890706227' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_build_table"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_build_keyval", "tomlf_constants", "tomlf_datetime", "tomlf_error", "tomlf_type" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_29]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/array.f90"' - fpm = fpm//NL//'digest = 5731959908631518546' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_build_array"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_build_keyval", "tomlf_error", "tomlf_type" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_30]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/build/path.f90"' - fpm = fpm//NL//'digest = 1001559863484583002' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_build_path"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_build_table", "tomlf_error", "tomlf_type" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_31]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/node.f90"' - fpm = fpm//NL//'digest = 4105605469572416054' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_structure_node"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = "tomlf_type_value"' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_32]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/array_list.f90"' - fpm = fpm//NL//'digest = 1707150725310470906' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_structure_array_list"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_33]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/ordered_map.f90"' - fpm = fpm//NL//'digest = 9194757273934069933' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_structure_ordered_map"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_34]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/map.f90"' - fpm = fpm//NL//'digest = 10697944851042277' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_structure_map"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.toml-f.sources.src_35]' - fpm = fpm//NL//'file-name = "build/dependencies/toml-f/src/tomlf/structure/list.f90"' - fpm = fpm//NL//'digest = 6018335058365199200' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "tomlf_structure_list"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_type_value" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' fpm = fpm//NL//'[packages.M_CLI2]' fpm = fpm//NL//'name = "M_CLI2"' fpm = fpm//NL//'version = "0.8.0"' @@ -2031,36 +1063,6 @@ subroutine fpm_081_table(table) fpm = fpm//NL//'modules-used = [ ]' fpm = fpm//NL//'include-dependencies = [ ]' fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.jonquil.sources.src_3]' - fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/ser.f90"' - fpm = fpm//NL//'digest = 2690773570566028936' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "jonquil_ser"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_error", "tomlf_utils" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.jonquil.sources.src_4]' - fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/lexer.f90"' - fpm = fpm//NL//'digest = 4057038173684122483' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "jonquil_lexer"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_utils" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' - fpm = fpm//NL//'[packages.jonquil.sources.src_5]' - fpm = fpm//NL//'file-name = "build/dependencies/jonquil/src/jonquil/parser.f90"' - fpm = fpm//NL//'digest = -2426842130572494815' - fpm = fpm//NL//'unit-scope = "FPM_SCOPE_LIB"' - fpm = fpm//NL//'unit-type = "FPM_UNIT_MODULE"' - fpm = fpm//NL//'modules-provided = "jonquil_parser"' - fpm = fpm//NL//'parent-modules = [ ]' - fpm = fpm//NL//'modules-used = [ "tomlf_constants", "tomlf_datetime", "tomlf_de_context" ]' - fpm = fpm//NL//'include-dependencies = [ ]' - fpm = fpm//NL//'link-libraries = [ ]' call string_to_toml(fpm, table) From 23e7c09293b099399f78f40d816e98e90f28844e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 10:06:15 +0200 Subject: [PATCH 293/799] set_value, add_table: unify fpm wrapper --- src/fpm/dependency.f90 | 41 +++------ src/fpm/git.f90 | 24 ++---- src/fpm/manifest/dependency.f90 | 48 +++-------- src/fpm/toml.f90 | 133 +++++++++++++++++++++++++++++ src/fpm_compiler.F90 | 50 +++-------- src/fpm_model.f90 | 147 +++++++++----------------------- 6 files changed, 216 insertions(+), 227 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index c78a758dd2..3458861242 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -66,7 +66,7 @@ module fpm_dependency use fpm_manifest_dependency, only: manifest_has_changed, dependency_destroy use fpm_strings, only: string_t, operator(.in.) use fpm_toml, only: toml_table, toml_key, toml_error, toml_serialize, & - get_value, set_value, add_table, toml_load, toml_stat + get_value, set_value, add_table, toml_load, toml_stat, set_string use fpm_versioning, only: version_t, new_version use fpm_settings, only: fpm_global_settings, get_global_settings, official_registry_base_url use fpm_downloader, only: downloader_t @@ -1466,35 +1466,16 @@ subroutine tree_dump_to_toml(self, table, error) type(toml_table), pointer :: ptr_deps,ptr character(27) :: unnamed - call set_value(table, "unit", self%unit, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set unit in TOML table') - return - end if - call set_value(table, "verbosity", self%verbosity, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set verbosity in TOML table') - return - end if - if (allocated(self%dep_dir)) then - call set_value(table, "dep-dir", self%dep_dir, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set dep-dir in TOML table') - return - end if - endif - if (allocated(self%cache)) then - call set_value(table, "cache", self%cache, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set cache in TOML table') - return - end if - endif - call set_value(table, "ndep", self%ndep, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set ndep in TOML table') - return - end if + call set_value(table, "unit", self%unit, error, 'dependency_tree_t') + if (allocated(error)) return + call set_value(table, "verbosity", self%verbosity, error, 'dependency_tree_t') + if (allocated(error)) return + call set_string(table, "dep-dir", self%dep_dir, error, 'dependency_tree_t') + if (allocated(error)) return + call set_string(table, "cache", self%cache, error, 'dependency_tree_t') + if (allocated(error)) return + call set_value(table, "ndep", self%ndep, error, 'dependency_tree_t') + if (allocated(error)) return if (allocated(self%dep)) then diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 64cca94188..471b1826fd 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -2,7 +2,7 @@ module fpm_git use fpm_error, only: error_t, fatal_error use fpm_filesystem, only : get_temp_filename, getline, join_path - use fpm_toml, only: serializable_t, toml_table, get_value, set_value, toml_stat + use fpm_toml, only: serializable_t, toml_table, get_value, set_value, toml_stat, set_string implicit none public :: git_target_t @@ -354,22 +354,12 @@ subroutine dump_to_toml(self, table, error) integer :: ierr - call set_value(table, "descriptor", descriptor_name(self%descriptor)) - if (allocated(self%url)) then - call set_value(table, "url", self%url, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'git_target_t: cannot set url in TOML table') - return - end if - endif - - if (allocated(self%object)) then - call set_value(table, "object", self%object, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'git_target_t: cannot set object in TOML table') - return - end if - endif + call set_string(table, "descriptor", descriptor_name(self%descriptor), error, 'git_target_t') + if (allocated(error)) return + call set_string(table, "url", self%url, error, 'git_target_t') + if (allocated(error)) return + call set_string(table, "object", self%object, error, 'git_target_t') + if (allocated(error)) return end subroutine dump_to_toml diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 1a14b2ef16..c3013178f5 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -27,7 +27,7 @@ module fpm_manifest_dependency use fpm_git, only: git_target_t, git_target_tag, git_target_branch, & & git_target_revision, git_target_default, operator(==), git_matches_manifest use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys, serializable_t, add_table, & - & set_value + & set_value, set_string use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS use fpm_versioning, only: version_t, new_version @@ -368,46 +368,20 @@ subroutine dump_to_toml(self, table, error) integer :: ierr - if (allocated(self%name)) then - call set_value(table, "name", self%name, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_config_t: cannot set name in TOML table') - return - end if - endif - - if (allocated(self%path)) then - call set_value(table, "path", self%path, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_config_t: cannot set path in TOML table') - return - end if - endif - - if (allocated(self%namespace)) then - call set_value(table, "namespace", self%namespace, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_config_t: cannot set namespace in TOML table') - return - end if - endif - + call set_string(table, "name", self%name, error, 'dependency_config_t') + if (allocated(error)) return + call set_string(table, "path", self%path, error, 'dependency_config_t') + if (allocated(error)) return + call set_string(table, "namespace", self%namespace, error, 'dependency_config_t') + if (allocated(error)) return if (allocated(self%requested_version)) then - call set_value(table, "requested_version", self%requested_version%s(), ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_config_t: cannot set requested_version in TOML table') - return - end if + call set_string(table, "requested_version", self%requested_version%s(), error, 'dependency_config_t') + if (allocated(error)) return endif - if (allocated(self%git)) then - call add_table(table, "git", ptr, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_config_t: cannot set git table in TOML table') - return - end if - + call add_table(table, "git", ptr, error) + if (allocated(error)) return call self%git%dump_to_toml(ptr, error) if (allocated(error)) return endif diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 5359edf4ea..fe2bae7b48 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -18,6 +18,7 @@ module fpm_toml use tomlf, only: toml_table, toml_array, toml_key, toml_stat, get_value, & & set_value, toml_parse, toml_error, new_table, add_table, add_array, & & toml_serialize, len, toml_load + use iso_fortran_env, only: int64 implicit none private @@ -51,6 +52,18 @@ module fpm_toml end type serializable_t + !> add_table: fpm interface + interface add_table + module procedure add_table_fpm + end interface add_table + + !> set_value: fpm interface + interface set_value + module procedure set_logical + module procedure set_integer + module procedure set_integer_64 + end interface set_value + interface set_string module procedure set_character module procedure set_string_type @@ -395,6 +408,93 @@ subroutine set_character(table, key, var, error, whereAt) end subroutine set_character + !> Function wrapper to set a logical variable to a toml table, returning an fpm error + subroutine set_logical(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> The key + character(len=*), intent(in) :: key + + !> The variable + logical, intent(in) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + call set_value(table, key, var, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot set logical key <'//key//'> in TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine set_logical + + !> Function wrapper to set a default integer variable to a toml table, returning an fpm error + subroutine set_integer(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> The key + character(len=*), intent(in) :: key + + !> The variable + integer, intent(in) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + call set_value(table, key, var, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot set integer key <'//key//'> in TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine set_integer + + !> Function wrapper to set a default integer variable to a toml table, returning an fpm error + subroutine set_integer_64(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> The key + character(len=*), intent(in) :: key + + !> The variable + integer(int64), intent(in) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + call set_value(table, key, var, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot set integer(int64) key <'//key//'> in TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine set_integer_64 + !> Function wrapper to set a character(len=:), allocatable variable to a toml table subroutine set_string_type(table, key, var, error, whereAt) @@ -417,6 +517,39 @@ subroutine set_string_type(table, key, var, error, whereAt) end subroutine set_string_type + !> Function wrapper to add a toml table and return an fpm error + subroutine add_table_fpm(table, key, ptr, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Table key + character(len=*), intent(in) :: key + + !> The character variable + type(toml_table), pointer, intent(out) :: ptr + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + !> Nullify pointer + nullify(ptr) + + call add_table(table, key, ptr, ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot add <'//key//'> table in TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine add_table_fpm + + !> Check if table contains only keys that are part of the list. If a key is !> found that is not part of the list, an error is allocated. subroutine check_keys(table, valid_keys, error) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index b8e3010777..187289c9dc 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -1191,29 +1191,15 @@ subroutine dump_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - integer :: ierr - !> Path to archiver call set_string(table, "ar", self%ar, error, 'archiver_t') if (allocated(error)) return - - call set_value(table, "use-response-file", self%use_response_file, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error dumping use_response_file') - return - end if - - call set_value(table, "echo", self%echo, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error dumping echo') - return - end if - - call set_value(table, "verbose", self%verbose, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error dumping verbose') - return - end if + call set_value(table, "use-response-file", self%use_response_file, error, 'archiver_t') + if (allocated(error)) return + call set_value(table, "echo", self%echo, error, 'archiver_t') + if (allocated(error)) return + call set_value(table, "verbose", self%verbose, error, 'archiver_t') + if (allocated(error)) return end subroutine dump_to_toml @@ -1294,30 +1280,18 @@ subroutine compiler_dump(self, table, error) integer :: ierr - call set_value(table, "id", self%id, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'compiler_t: error dumping id') - return - end if - + call set_value(table, "id", self%id, error, 'compiler_t') + if (allocated(error)) return call set_string(table, "fc", self%fc, error, 'compiler_t') if (allocated(error)) return call set_string(table, "cc", self%cc, error, 'compiler_t') if (allocated(error)) return call set_string(table, "cxx", self%cxx, error, 'compiler_t') if (allocated(error)) return - - call set_value(table, "echo", self%echo, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error dumping echo') - return - end if - - call set_value(table, "verbose", self%verbose, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error dumping verbose') - return - end if + call set_value(table, "echo", self%echo, error, 'compiler_t') + if (allocated(error)) return + call set_value(table, "verbose", self%verbose, error, 'compiler_t') + if (allocated(error)) return end subroutine compiler_dump diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index 18110571a7..4732fe48ed 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -515,44 +515,29 @@ subroutine srcfile_dump_to_toml(self, table, error) integer :: ierr - if (allocated(self%file_name)) then - call set_value(table, "file-name", self%file_name, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'srcfile_t: cannot set file-name in TOML table') - return - end if - endif - - if (allocated(self%exe_name)) then - call set_value(table, "exe-name", self%exe_name, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'srcfile_t: cannot set exe-name in TOML table') - return - end if - endif - - call set_value(table,"digest",self%digest) + call set_string(table, "file-name", self%file_name, error, 'srcfile_t') + if (allocated(error)) return + call set_string(table, "exe-name", self%exe_name, error, 'srcfile_t') + if (allocated(error)) return + call set_value(table, "digest", self%digest, error, 'srcfile_t') + if (allocated(error)) return ! unit_scope and unit_type are saved as strings so the output is independent ! of the internal representation - call set_value(table,"unit-scope",FPM_SCOPE_NAME(self%unit_scope)) - call set_value(table,"unit-type",FPM_UNIT_NAME(self%unit_type)) - - call set_list(table,"modules-provided",self%modules_provided, error) + call set_string(table,"unit-scope",FPM_SCOPE_NAME(self%unit_scope), error, 'srcfile_t') if (allocated(error)) return - - call set_list(table,"parent-modules",self%parent_modules, error) + call set_string(table,"unit-type",FPM_UNIT_NAME(self%unit_type), error, 'srcfile_t') if (allocated(error)) return - - call set_list(table,"modules-used",self%modules_used, error) + call set_list(table, "modules-provided",self%modules_provided, error) if (allocated(error)) return - - call set_list(table,"include-dependencies",self%include_dependencies, error) + call set_list(table, "parent-modules",self%parent_modules, error) if (allocated(error)) return - - call set_list(table,"link-libraries",self%link_libraries, error) + call set_list(table, "modules-used",self%modules_used, error) + if (allocated(error)) return + call set_list(table, "include-dependencies",self%include_dependencies, error) + if (allocated(error)) return + call set_list(table, "link-libraries",self%link_libraries, error) if (allocated(error)) return - end subroutine srcfile_dump_to_toml @@ -640,27 +625,12 @@ subroutine fft_dump_to_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - integer :: ierr - - call set_value(table, "implicit-typing", self%implicit_typing, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fortran_features_t: cannot set implicit-typing in TOML table') - return - end if - - call set_value(table, "implicit-external", self%implicit_external, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fortran_features_t: cannot set implicit-external in TOML table') - return - end if - - if (allocated(self%source_form)) then - call set_value(table, "source-form", self%source_form, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fortran_features_t: cannot set source-form in TOML table') - return - end if - endif + call set_value(table, "implicit-typing", self%implicit_typing, error, 'fortran_features_t') + if (allocated(error)) return + call set_value(table, "implicit-external", self%implicit_external, error, 'fortran_features_t') + if (allocated(error)) return + call set_string(table, "source-form", self%source_form, error, 'fortran_features_t') + if (allocated(error)) return end subroutine fft_dump_to_toml @@ -758,11 +728,8 @@ subroutine package_dump_to_toml(self, table, error) call set_string(table, "version", self%version, error, 'package_t') if (allocated(error)) return - call set_value(table, "module-naming", self%enforce_module_names, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'package_t: cannot set module-naming in TOML table') - return - end if + call set_value(table, "module-naming", self%enforce_module_names, error, 'package_t') + if (allocated(error)) return call set_string(table, "module-prefix", self%module_prefix, error, 'package_t') if (allocated(error)) return @@ -771,33 +738,22 @@ subroutine package_dump_to_toml(self, table, error) if (allocated(error)) return !> Create a fortran table - call add_table(table, "fortran", ptr, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'package_t: cannot set fortran table in TOML table') - return - end if + call add_table(table, "fortran", ptr, error, 'package_t') + if (allocated(error)) return call self%features%dump_to_toml(ptr, error) if (allocated(error)) return !> Create a sources table if (allocated(self%sources)) then - call add_table(table, "sources", ptr, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'package_t: cannot set sources table in TOML table') - return - end if + call add_table(table, "sources", ptr, error, 'package_t') + if (allocated(error)) return do ii = 1, size(self%sources) write(src_name,1) ii - call add_table(ptr, trim(src_name), this_source) - - if (.not. associated(this_source)) then - call fatal_error(error, "package_t cannot create entry for source "//trim(src_name)) - return - end if - + call add_table(ptr, trim(src_name), this_source, error, 'package_t[source]') + if (allocated(error)) return call self%sources(ii)%dump_to_toml(this_source,error) if (allocated(error)) return @@ -950,19 +906,13 @@ subroutine model_dump_to_toml(self, table, error) call set_string(table, "package-name", self%package_name, error, 'fpm_model_t') if (allocated(error)) return - call add_table(table, "compiler", ptr, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'package_t: cannot set compiler table') - return - end if + call add_table(table, "compiler", ptr, error, 'fpm_model_t') + if (allocated(error)) return call self%compiler%dump_to_toml(ptr, error) if (allocated(error)) return - call add_table(table, "archiver", ptr, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'package_t: cannot set archiver table') - return - end if + call add_table(table, "archiver", ptr, error, 'fpm_model_t') + if (allocated(error)) return call self%archiver%dump_to_toml(ptr, error) if (allocated(error)) return @@ -983,25 +933,15 @@ subroutine model_dump_to_toml(self, table, error) call set_list(table, "external-modules", self%external_modules, error) if (allocated(error)) return - call set_value(table, "include-tests", self%include_tests, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fpm_model_t: cannot set include-tests in TOML table') - return - end if - - call set_value(table, "module-naming", self%enforce_module_names, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fpm_model_t: cannot set module-naming in TOML table') - return - end if + call set_value(table, "include-tests", self%include_tests, error, 'fpm_model_t') + if (allocated(error)) return + call set_value(table, "module-naming", self%enforce_module_names, error, 'fpm_model_t') + if (allocated(error)) return call set_string(table, "module-prefix", self%module_prefix, error, 'fpm_model_t') if (allocated(error)) return - call add_table(table, "deps", ptr, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fpm_model_t: cannot set dependencies table') - return - end if + call add_table(table, "deps", ptr, error, 'fpm_model_t') + if (allocated(error)) return call self%deps%dump_to_toml(ptr, error) if (allocated(error)) return @@ -1023,14 +963,11 @@ subroutine model_dump_to_toml(self, table, error) !> So, serialization will work regardless of size(self%dep) == self%ndep if (len_trim(pkg%name)==0) then write(unnamed,1) ii - call add_table(ptr_pkg, trim(unnamed), ptr) + call add_table(ptr_pkg, trim(unnamed), ptr, error, 'fpm_model_t[package]') else - call add_table(ptr_pkg, pkg%name, ptr) - end if - if (.not. associated(ptr)) then - call fatal_error(error, "fpm_model_t cannot create entry for package "//pkg%name) - return + call add_table(ptr_pkg, pkg%name, ptr, error, 'fpm_model_t[package]') end if + if (allocated(error)) return call pkg%dump_to_toml(ptr, error) if (allocated(error)) return From 5c95577972f86029f9a14025871e84933c29ee41 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 10:14:34 +0200 Subject: [PATCH 294/799] unify get_value interface --- src/fpm/dependency.f90 | 29 +++---------- src/fpm/toml.f90 | 93 ++++++++++++++++++++++++++++++++++++++++++ src/fpm_compiler.F90 | 51 ++++++----------------- src/fpm_model.f90 | 47 ++++++--------------- 4 files changed, 123 insertions(+), 97 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 3458861242..2278b4d8d1 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1358,29 +1358,12 @@ subroutine node_load_from_toml(self, table, error) call self%dependency_config_t%load_from_toml(table, error) if (allocated(error)) return - call get_value(table, "done", self%done, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot read done flag in TOML table') - return - end if - - call get_value(table, "update", self%update, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot read update flag in TOML table') - return - end if - - call get_value(table, "cached", self%cached, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot read cached flag in TOML table') - return - end if - - call get_value(table, "done", self%done, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot read done flag in TOML table') - return - end if + call get_value(table, "done", self%done, error, 'dependency_node_t') + if (allocated(error)) return + call get_value(table, "update", self%update, error, 'dependency_node_t') + if (allocated(error)) return + call get_value(table, "cached", self%cached, error, 'dependency_node_t') + if (allocated(error)) return call get_value(table, "proj-dir", self%proj_dir) call get_value(table, "revision", self%revision) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index fe2bae7b48..ba3bc62749 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -69,6 +69,13 @@ module fpm_toml module procedure set_string_type end interface set_string + !> get_value: fpm interface + interface get_value + module procedure get_logical + module procedure get_integer + module procedure get_integer_64 + end interface get_value + abstract interface @@ -549,6 +556,92 @@ subroutine add_table_fpm(table, key, ptr, error, whereAt) end subroutine add_table_fpm + !> Function wrapper to get a logical variable from a toml table, returning an fpm error + subroutine get_logical(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> The key + character(len=*), intent(in) :: key + + !> The variable + logical, intent(inout) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + call get_value(table, key, var, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot get logical key <'//key//'> from TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine get_logical + + !> Function wrapper to get a default integer variable from a toml table, returning an fpm error + subroutine get_integer(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> The key + character(len=*), intent(in) :: key + + !> The variable + integer, intent(inout) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + call get_value(table, key, var, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot get integer key <'//key//'> from TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine get_integer + + !> Function wrapper to get a integer(int64) variable from a toml table, returning an fpm error + subroutine get_integer_64(table, key, var, error, whereAt) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> The key + character(len=*), intent(in) :: key + + !> The variable + integer(int64), intent(inout) :: var + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Optional description + character(len=*), intent(in), optional :: whereAt + + integer :: ierr + + call get_value(table, key, var, stat=ierr) + if (ierr/=toml_stat%success) then + call fatal_error(error,'cannot get integer(int64) key <'//key//'> from TOML table') + if (present(whereAt)) error%message = whereAt//': '//error%message + return + end if + + end subroutine get_integer_64 !> Check if table contains only keys that are part of the list. If a key is !> found that is not part of the list, an error is allocated. diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 187289c9dc..99fda31e30 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -1215,27 +1215,14 @@ subroutine load_from_toml(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - integer :: ierr - call get_value(table, "ar", self%ar) - call get_value(table, "use-response-file", self%use_response_file, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error getting use_response_file from TOML') - return - end if - - call get_value(table, "echo", self%echo, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error getting echo from TOML') - return - end if - - call get_value(table, "verbose", self%verbose, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'archiver_t: error getting verbose from TOML') - return - end if + call get_value(table, "use-response-file", self%use_response_file, error, 'archiver_t') + if (allocated(error)) return + call get_value(table, "echo", self%echo, error, 'archiver_t') + if (allocated(error)) return + call get_value(table, "verbose", self%verbose, error, 'archiver_t') + if (allocated(error)) return end subroutine load_from_toml @@ -1307,29 +1294,15 @@ subroutine compiler_load(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - integer :: ierr - - call get_value(table, "id", self%id, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'compiler_t: error getting id from TOML') - return - end if - + call get_value(table, "id", self%id, error, 'compiler_t') + if (allocated(error)) return call get_value(table, "fc", self%fc) call get_value(table, "cc", self%cc) call get_value(table, "cxx", self%cxx) - - call get_value(table, "echo", self%echo, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'compiler_t: error getting echo from TOML') - return - end if - - call get_value(table, "verbose", self%verbose, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'compiler_t: error getting verbose from TOML') - return - end if + call get_value(table, "echo", self%echo, error, 'compiler_t') + if (allocated(error)) return + call get_value(table, "verbose", self%verbose, error, 'compiler_t') + if (allocated(error)) return end subroutine compiler_load diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index 4732fe48ed..e34f955246 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -558,12 +558,8 @@ subroutine srcfile_load_from_toml(self, table, error) call get_value(table, "file-name", self%file_name) call get_value(table, "exe-name", self%exe_name) - - call get_value(table, "digest", self%digest, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'srcfile_t: cannot set digest in TOML table') - return - end if + call get_value(table, "digest", self%digest, error, 'srcfile_t') + if (allocated(error)) return ! unit_scope and unit_type are saved as strings so the output is independent ! of the internal representation @@ -648,18 +644,10 @@ subroutine fft_load_from_toml(self, table, error) integer :: ierr - call get_value(table, "implicit-typing", self%implicit_typing, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fortran_features_t: cannot read implicit-typing from TOML table') - return - end if - - call get_value(table, "implicit-external", self%implicit_external, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fortran_features_t: cannot read implicit-typing from TOML table') - return - end if - + call get_value(table, "implicit-typing", self%implicit_typing, error, 'fortran_features_t') + if (allocated(error)) return + call get_value(table, "implicit-external", self%implicit_external, error, 'fortran_features_t') + if (allocated(error)) return ! Return unallocated value if not present call get_value(table, "source-form", self%source_form) @@ -785,11 +773,8 @@ subroutine package_load_from_toml(self, table, error) call get_value(table, "name", self%name) call get_value(table, "version", self%version) - call get_value(table, "module-naming", self%enforce_module_names, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'package_t: cannot get module-naming from TOML table') - return - end if + call get_value(table, "module-naming", self%enforce_module_names, error, 'package_t') + if (allocated(error)) return ! Return unallocated value if not present call get_value(table, "module-prefix", self%module_prefix%s) @@ -1076,18 +1061,10 @@ subroutine model_load_from_toml(self, table, error) if (allocated(error)) return call get_list(table, "external-modules", self%external_modules, error) if (allocated(error)) return - - call get_value(table, "include-tests", self%include_tests, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fpm_model_t: cannot read include-tests in TOML table') - return - end if - - call get_value(table, "module-naming", self%enforce_module_names, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'fpm_model_t: cannot set module-naming in TOML table') - return - end if + call get_value(table, "include-tests", self%include_tests, error, 'fpm_model_t') + if (allocated(error)) return + call get_value(table, "module-naming", self%enforce_module_names, error, 'fpm_model_t') + if (allocated(error)) return call get_value(table, "module-prefix", self%module_prefix%s) end subroutine model_load_from_toml From 73687758d4eb6b1d02f18037003193eab621abf5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 10:53:33 +0200 Subject: [PATCH 295/799] add JSON serialization flag; keep it unactive --- fpm.toml | 2 +- src/fpm/git.f90 | 2 +- src/fpm/toml.f90 | 83 ++++++++++++++++++++++++++++++------- test/fpm_test/test_toml.f90 | 5 +-- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/fpm.toml b/fpm.toml index e59941e417..bf45c7285a 100644 --- a/fpm.toml +++ b/fpm.toml @@ -11,7 +11,7 @@ toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" jonquil.git = "https://github.com/toml-f/jonquil" -jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" +jonquil.rev = "4c27c8c1e411fa8790dffcf8c3fa7a27b6322273" [[test]] name = "cli-test" diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 471b1826fd..14d1717ecc 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -382,7 +382,7 @@ subroutine load_from_toml(self, table, error) self%descriptor = parse_descriptor(descriptor_name) if (self%descriptor==git_descriptor%error) then - call fatal_error(error,"invalid descriptor ID in TOML entry") + call fatal_error(error,"invalid descriptor ID <"//descriptor_name//"> in TOML entry") return end if diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index ba3bc62749..7c85fe83c3 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -18,6 +18,9 @@ module fpm_toml use tomlf, only: toml_table, toml_array, toml_key, toml_stat, get_value, & & set_value, toml_parse, toml_error, new_table, add_table, add_array, & & toml_serialize, len, toml_load + use tomlf_de_parser, only: parse + use jonquil, only: json_serialize, json_error, json_value, json_object, json_load + use jonquil_lexer, only: json_lexer, new_lexer_from_unit use iso_fortran_env, only: int64 implicit none private @@ -154,51 +157,69 @@ end subroutine test_serialization !> Write serializable object to a formatted Fortran unit - subroutine dump_to_unit(self, unit, error) + subroutine dump_to_unit(self, unit, error, json) !> Instance of the dependency tree class(serializable_t), intent(inout) :: self !> Formatted unit integer, intent(in) :: unit !> Error handling type(error_t), allocatable, intent(out) :: error + !> Optional JSON format requested? + logical, optional, intent(in) :: json type(toml_table) :: table + logical :: is_json + + is_json = .false.; if (present(json)) is_json = json table = toml_table() call self%dump(table, error) - write (unit, '(a)') toml_serialize(table) + if (is_json) then + + !> Deactivate JSON serialization for now + call fatal_error(error, 'JSON serialization option is not yet available') + return + + write (unit, '(a)') json_serialize(table) + else + write (unit, '(a)') toml_serialize(table) + end if call table%destroy() end subroutine dump_to_unit !> Write serializable object to file - subroutine dump_to_file(self, file, error) + subroutine dump_to_file(self, file, error, json) !> Instance of the dependency tree class(serializable_t), intent(inout) :: self !> File name character(len=*), intent(in) :: file !> Error handling type(error_t), allocatable, intent(out) :: error + !> Optional JSON format + logical, optional, intent(in) :: json integer :: unit open (file=file, newunit=unit) - call self%dump(unit, error) + call self%dump(unit, error, json) close (unit) if (allocated(error)) return end subroutine dump_to_file !> Read dependency tree from file - subroutine load_from_file(self, file, error) + subroutine load_from_file(self, file, error, json) !> Instance of the dependency tree class(serializable_t), intent(inout) :: self !> File name character(len=*), intent(in) :: file !> Error handling type(error_t), allocatable, intent(out) :: error + !> Optional JSON format + logical, optional, intent(in) :: json integer :: unit logical :: exist @@ -207,30 +228,64 @@ subroutine load_from_file(self, file, error) if (.not. exist) return open (file=file, newunit=unit) - call self%load(unit, error) + call self%load(unit, error, json) close (unit) end subroutine load_from_file !> Read dependency tree from file - subroutine load_from_unit(self, unit, error) + subroutine load_from_unit(self, unit, error, json) !> Instance of the dependency tree class(serializable_t), intent(inout) :: self !> File name integer, intent(in) :: unit !> Error handling type(error_t), allocatable, intent(out) :: error + !> Optional JSON format + logical, optional, intent(in) :: json - type(toml_error), allocatable :: parse_error + type(toml_error), allocatable :: toml_error type(toml_table), allocatable :: table + type(json_lexer) :: lexer + logical :: is_json - call toml_load(table, unit, error=parse_error) + is_json = .false.; if (present(json)) is_json = json - if (allocated(parse_error)) then - allocate (error) - call move_alloc(parse_error%message, error%message) - return - end if + if (is_json) then + + !> Deactivate JSON deserialization for now + call fatal_error(error, 'JSON deserialization option is not yet available') + return + + !> init JSON interpreter + call new_lexer_from_unit(lexer, unit, toml_error) + if (allocated(toml_error)) then + allocate (error) + call move_alloc(toml_error%message, error%message) + return + end if + + !> Parse JSON to TOML table + call parse(lexer, table, error=toml_error) + if (allocated(toml_error)) then + allocate (error) + call move_alloc(toml_error%message, error%message) + return + end if + + else + + !> use default TOML parser + call toml_load(table, unit, error=toml_error) + + if (allocated(toml_error)) then + allocate (error) + call move_alloc(toml_error%message, error%message) + return + end if + + endif + !> Read object from TOML table call self%load(table, error) if (allocated(error)) return diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index ed54db07a5..fa17b2fea3 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -404,10 +404,7 @@ subroutine dependency_tree_roundtrip(error) sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") call deps%test_serialization("full dependency tree", error) - if (allocated(error)) then - print *, error%message - stop 'catastrophic' - end if + if (allocated(error)) return ! Remove dependencies (including all them) do ii = 1, ALLOCATED_DEPS From 82a4b28a0e70750346b6b7ba0dac284bf61f10da Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 11:44:59 +0200 Subject: [PATCH 296/799] implement CLI for build, update --- src/fpm.f90 | 6 ++++++ src/fpm/cmd/update.f90 | 7 ++++++- src/fpm_command_line.f90 | 32 +++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 5247f9e58d..c44452e705 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -418,6 +418,12 @@ subroutine cmd_build(settings) call fpm_stop(1,'*cmd_build* Target error: '//error%message) end if +!> Dump model to file +if (len_trim(settings%dump)>0) then + call model%dump(trim(settings%dump),error) + if (allocated(error)) call fpm_stop(1,'*cmd_build* Model dump error: '//error%message) +endif + if(settings%list)then do i=1,size(targets) write(stderr,*) targets(i)%ptr%output_file diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index e1bcb7326c..53377b113a 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -59,6 +59,11 @@ subroutine cmd_update(settings) end do end if + if (len_trim(settings%dump)>0) then + call deps%dump(trim(settings%dump), error) + call handle_error(error) + end if + end subroutine cmd_update !> Error handling for this command @@ -66,7 +71,7 @@ subroutine handle_error(error) !> Potential error type(error_t), intent(in), optional :: error if (present(error)) then - call fpm_stop(1, error%message) + call fpm_stop(1, '*cmd_update* error: '//error%message) end if end subroutine handle_error diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 4a434deb4b..8595e89591 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -73,6 +73,7 @@ module fpm_command_line logical :: show_model=.false. logical :: build_tests=.false. logical :: prune=.true. + character(len=:),allocatable :: dump character(len=:),allocatable :: compiler character(len=:),allocatable :: c_compiler character(len=:),allocatable :: cxx_compiler @@ -105,6 +106,7 @@ module fpm_command_line !> Settings for interacting and updating with project dependencies type, extends(fpm_cmd_settings) :: fpm_update_settings character(len=ibug),allocatable :: name(:) + character(len=:),allocatable :: dump logical :: fetch_only logical :: clean end type @@ -132,7 +134,7 @@ module fpm_command_line & 'test', 'runner', 'install', 'update', 'list', 'help', 'version' ] character(len=:), allocatable :: val_runner, val_compiler, val_flag, val_cflag, val_cxxflag, val_ldflag, & - val_profile + val_profile, val_dump ! '12345678901234567890123456789012345678901234567890123456789012345678901234567890',& character(len=80), parameter :: help_text_build_common(*) = [character(len=80) :: & @@ -331,6 +333,7 @@ subroutine get_command_line_settings(cmd_settings) call set_args(common_args // compiler_args //'& & --list F & & --show-model F & + & --dump " " & & --tests F & & --',help_build,version_text) @@ -339,9 +342,14 @@ subroutine get_command_line_settings(cmd_settings) c_compiler = sget('c-compiler') cxx_compiler = sget('cxx-compiler') archiver = sget('archiver') + + val_dump = sget('dump') + if (specified('dump') .and. val_dump=='')val_dump='fpm_model.toml' + allocate( fpm_build_settings :: cmd_settings ) cmd_settings=fpm_build_settings( & & profile=val_profile,& + & dump=val_dump,& & prune=.not.lget('no-prune'), & & compiler=val_compiler, & & c_compiler=c_compiler, & @@ -574,7 +582,7 @@ subroutine get_command_line_settings(cmd_settings) & verbose=lget('verbose') ) case('update') - call set_args(common_args // ' --fetch-only F --clean F', & + call set_args(common_args // ' --fetch-only F --clean F --dump " " ', & help_update, version_text) if( size(unnamed) > 1 )then @@ -583,8 +591,11 @@ subroutine get_command_line_settings(cmd_settings) names=[character(len=len(names)) :: ] endif + val_dump = sget('dump') + if (specified('dump') .and. val_dump=='')val_dump='fpm_dependencies.toml' + allocate(fpm_update_settings :: cmd_settings) - cmd_settings=fpm_update_settings(name=names, & + cmd_settings=fpm_update_settings(name=names, dump=val_dump, & fetch_only=lget('fetch-only'), verbose=lget('verbose'), & clean=lget('clean')) @@ -691,11 +702,11 @@ subroutine set_help() help_list_dash = [character(len=80) :: & ' ', & ' build [--compiler COMPILER_NAME] [--profile PROF] [--flag FFLAGS] [--list] ', & - ' [--tests] [--no-prune] ', & + ' [--tests] [--no-prune] [--dump [TOMLFILE]] ', & ' help [NAME(s)] ', & ' new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & ' [--full|--bare][--backfill] ', & - ' update [NAME(s)] [--fetch-only] [--clean] [--verbose] ', & + ' update [NAME(s)] [--fetch-only] [--clean] [--verbose] [--dump [TOMLFILE]] ', & ' list [--list] ', & ' run [[--target] NAME(s) [--example] [--profile PROF] [--flag FFLAGS] [--all] ', & ' [--runner "CMD"] [--compiler COMPILER_NAME] [--list] [-- ARGS] ', & @@ -812,10 +823,10 @@ subroutine set_help() ' Their syntax is ', & ' ', & ' build [--profile PROF] [--flag FFLAGS] [--list] [--compiler COMPILER_NAME] ', & - ' [--tests] [--no-prune] ', & + ' [--tests] [--no-prune] [--dump [TOMLFILE]] ', & ' new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & ' [--full|--bare][--backfill] ', & - ' update [NAME(s)] [--fetch-only] [--clean] ', & + ' update [NAME(s)] [--fetch-only] [--clean] [--dump [TOMLFILE]] ', & ' run [[--target] NAME(s)] [--profile PROF] [--flag FFLAGS] [--list] [--all] ', & ' [--example] [--runner "CMD"] [--compiler COMPILER_NAME] ', & ' [--no-prune] [-- ARGS] ', & @@ -998,7 +1009,7 @@ subroutine set_help() ' ', & 'SYNOPSIS ', & ' fpm build [--profile PROF] [--flag FFLAGS] [--compiler COMPILER_NAME] ', & - ' [--list] [--tests] ', & + ' [--list] [--tests] [--dump [TOMLFILE]] ', & ' ', & ' fpm build --help|--version ', & ' ', & @@ -1026,6 +1037,8 @@ subroutine set_help() ' --list list candidates instead of building or running them ', & ' --tests build all tests (otherwise only if needed) ', & ' --show-model show the model and exit (do not build) ', & + ' --dump [TOMLFILE] save model representation to TOMLFILE ', & + ' (default file name: model.toml) ', & ' --help print this help and exit ', & ' --version print program version information and exit ', & ' ', & @@ -1225,7 +1238,7 @@ subroutine set_help() ' update(1) - manage project dependencies', & '', & 'SYNOPSIS', & - ' fpm update [--fetch-only] [--clean] [--verbose] [NAME(s)]', & + ' fpm update [--fetch-only] [--clean] [--verbose] [--dump [TOMLFILE]] [NAME(s)]', & '', & 'DESCRIPTION', & ' Manage and update project dependencies. If no dependency names are', & @@ -1235,6 +1248,7 @@ subroutine set_help() ' --fetch-only Only fetch dependencies, do not update existing projects', & ' --clean Do not use previous dependency cache', & ' --verbose Show additional printout', & + ' --dump [TOMLFILE] Dump updated dependency tree to a toml file (default: fpm_dependencies.toml)', & '', & 'SEE ALSO', & ' The fpm(1) home page at https://github.com/fortran-lang/fpm', & From 1899e48ab7b9c5e396c634c5a97639dfba4ba04d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 14:53:27 +0200 Subject: [PATCH 297/799] enable JSON serialization --- src/fpm/dependency.f90 | 7 ++-- src/fpm/toml.f90 | 83 ++++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 2278b4d8d1..6b700f0835 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1516,19 +1516,20 @@ subroutine tree_load_from_toml(self, table, error) call get_value(table, "unit", self%unit, stat=ierr) if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set in TOML table') + print *, 'unit=',self%unit,' ierr=',ierr + call fatal_error(error,'dependency_tree_t: cannot get in TOML table') return end if call get_value(table, "verbosity", self%verbosity, stat=ierr) if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set in TOML table') + call fatal_error(error,'dependency_tree_t: cannot get in TOML table') return end if call get_value(table, "ndep", self%ndep, stat=ierr) if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot set in TOML table') + call fatal_error(error,'dependency_tree_t: cannot get in TOML table') return end if diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 7c85fe83c3..07c81cebf2 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -17,10 +17,10 @@ module fpm_toml use fpm_strings, only: string_t use tomlf, only: toml_table, toml_array, toml_key, toml_stat, get_value, & & set_value, toml_parse, toml_error, new_table, add_table, add_array, & - & toml_serialize, len, toml_load + & toml_serialize, len, toml_load, toml_value use tomlf_de_parser, only: parse - use jonquil, only: json_serialize, json_error, json_value, json_object, json_load - use jonquil_lexer, only: json_lexer, new_lexer_from_unit + use jonquil, only: json_serialize, json_error, json_value, json_object, json_load, & + cast_to_object use iso_fortran_env, only: int64 implicit none private @@ -130,28 +130,40 @@ subroutine test_serialization(self, message, error) character(len=*), intent(in) :: message type(error_t), allocatable, intent(out) :: error - integer :: iunit + integer :: iunit, ii class(serializable_t), allocatable :: copy + character(len=4), parameter :: formats(2) = ['TOML','JSON'] - open(newunit=iunit,form='formatted',status='scratch') + all_formats: do ii = 1, 2 - !> Dump to scratch file - call self%dump(iunit, error) - if (allocated(error)) return + open(newunit=iunit,form='formatted',status='scratch') - !> Load from scratch file - rewind(iunit) - allocate(copy,mold=self) - call copy%load(iunit,error) - if (allocated(error)) return - close(iunit) + !> Dump to scratch file + call self%dump(iunit, error, json=ii==2) + if (allocated(error)) then + error%message = formats(ii)//': '//error%message + return + endif + + !> Load from scratch file + rewind(iunit) + allocate(copy,mold=self) + call copy%load(iunit,error, json=ii==2) + if (allocated(error)) then + error%message = formats(ii)//': '//error%message + return + endif + close(iunit) - !> Check same - if (.not.(self==copy)) then - call fatal_error(error,'serializable object failed TOML write/reread test: '//trim(message)) - return - end if - deallocate(copy) + !> Check same + if (.not.(self==copy)) then + call fatal_error(error,'serializable object failed '//formats(ii)//& + ' write/reread test: '//trim(message)) + return + end if + deallocate(copy) + + end do all_formats end subroutine test_serialization @@ -177,9 +189,9 @@ subroutine dump_to_unit(self, unit, error, json) if (is_json) then - !> Deactivate JSON serialization for now - call fatal_error(error, 'JSON serialization option is not yet available') - return +! !> Deactivate JSON serialization for now +! call fatal_error(error, 'JSON serialization option is not yet available') +! return write (unit, '(a)') json_serialize(table) else @@ -245,33 +257,31 @@ subroutine load_from_unit(self, unit, error, json) type(toml_error), allocatable :: toml_error type(toml_table), allocatable :: table - type(json_lexer) :: lexer + type(toml_table), pointer :: jtable + class(toml_value), allocatable :: object logical :: is_json is_json = .false.; if (present(json)) is_json = json if (is_json) then - !> Deactivate JSON deserialization for now - call fatal_error(error, 'JSON deserialization option is not yet available') - return - !> init JSON interpreter - call new_lexer_from_unit(lexer, unit, toml_error) + call json_load(object, unit, error=toml_error) if (allocated(toml_error)) then allocate (error) call move_alloc(toml_error%message, error%message) return end if - !> Parse JSON to TOML table - call parse(lexer, table, error=toml_error) - if (allocated(toml_error)) then - allocate (error) - call move_alloc(toml_error%message, error%message) + jtable => cast_to_object(object) + if (.not.associated(jtable)) then + call fatal_error(error,'cannot initialize JSON table ') return end if + !> Read object from TOML table + call self%load(jtable, error) + else !> use default TOML parser @@ -283,10 +293,11 @@ subroutine load_from_unit(self, unit, error, json) return end if + !> Read object from TOML table + call self%load(table, error) + endif - !> Read object from TOML table - call self%load(table, error) if (allocated(error)) return end subroutine load_from_unit From 1b6c795cba438767ad04108dec45c57c0ee0b1f7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 15:01:51 +0200 Subject: [PATCH 298/799] `--dump` option: choose JSON or TOML based on filename extension --- src/fpm.f90 | 3 ++- src/fpm/cmd/update.f90 | 3 ++- src/fpm/toml.f90 | 19 +++++++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index c44452e705..2cb265cc3b 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -21,6 +21,7 @@ module fpm FPM_TARGET_EXECUTABLE, FPM_TARGET_ARCHIVE use fpm_manifest, only : get_package_data, package_config_t use fpm_error, only : error_t, fatal_error, fpm_stop +use fpm_toml, only: name_is_json use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, & & stdout=>output_unit, & & stderr=>error_unit @@ -420,7 +421,7 @@ subroutine cmd_build(settings) !> Dump model to file if (len_trim(settings%dump)>0) then - call model%dump(trim(settings%dump),error) + call model%dump(trim(settings%dump),error,json=name_is_json(trim(settings%dump))) if (allocated(error)) call fpm_stop(1,'*cmd_build* Model dump error: '//error%message) endif diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index 53377b113a..11ca717441 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -4,6 +4,7 @@ module fpm_cmd_update use fpm_error, only : error_t, fpm_stop use fpm_filesystem, only : exists, mkdir, join_path, delete_file, filewrite use fpm_manifest, only : package_config_t, get_package_data + use fpm_toml, only: name_is_json implicit none private public :: cmd_update @@ -60,7 +61,7 @@ subroutine cmd_update(settings) end if if (len_trim(settings%dump)>0) then - call deps%dump(trim(settings%dump), error) + call deps%dump(trim(settings%dump), error, json=name_is_json(trim(settings%dump))) call handle_error(error) end if diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 07c81cebf2..5a1508669b 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -14,7 +14,7 @@ !> [TOML-Fortran](https://toml-f.github.io/toml-f) developer pages. module fpm_toml use fpm_error, only: error_t, fatal_error, file_not_found_error - use fpm_strings, only: string_t + use fpm_strings, only: string_t, str_ends_with, lower use tomlf, only: toml_table, toml_array, toml_key, toml_stat, get_value, & & set_value, toml_parse, toml_error, new_table, add_table, add_array, & & toml_serialize, len, toml_load, toml_value @@ -27,7 +27,8 @@ module fpm_toml public :: read_package_file, toml_table, toml_array, toml_key, toml_stat, & get_value, set_value, get_list, new_table, add_table, add_array, len, & - toml_error, toml_serialize, toml_load, check_keys, set_list, set_string + toml_error, toml_serialize, toml_load, check_keys, set_list, set_string, & + name_is_json !> An abstract interface for any fpm class that should be fully serializable to/from TOML/JSON type, abstract, public :: serializable_t @@ -754,4 +755,18 @@ subroutine check_keys(table, valid_keys, error) end subroutine check_keys + !> Choose between JSON or TOML based on a file name + logical function name_is_json(filename) + character(*), intent(in) :: filename + + character(*), parameter :: json_identifier = ".json" + + name_is_json = .false. + + if (len_trim(filename) Date: Thu, 13 Apr 2023 15:29:31 +0200 Subject: [PATCH 299/799] update CLI help to JSON/TOML choice --- src/fpm_command_line.f90 | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 565eb2661c..791dbe5645 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -702,11 +702,11 @@ subroutine set_help() help_list_dash = [character(len=80) :: & ' ', & ' build [--compiler COMPILER_NAME] [--profile PROF] [--flag FFLAGS] [--list] ', & - ' [--tests] [--no-prune] [--dump [TOMLFILE]] ', & + ' [--tests] [--no-prune] [--dump [FILENAME]] ', & ' help [NAME(s)] ', & ' new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & ' [--full|--bare][--backfill] ', & - ' update [NAME(s)] [--fetch-only] [--clean] [--verbose] [--dump [TOMLFILE]] ', & + ' update [NAME(s)] [--fetch-only] [--clean] [--verbose] [--dump [FILENAME]] ', & ' list [--list] ', & ' run [[--target] NAME(s) [--example] [--profile PROF] [--flag FFLAGS] [--all] ', & ' [--runner "CMD"] [--compiler COMPILER_NAME] [--list] [-- ARGS] ', & @@ -823,10 +823,10 @@ subroutine set_help() ' Their syntax is ', & ' ', & ' build [--profile PROF] [--flag FFLAGS] [--list] [--compiler COMPILER_NAME] ', & - ' [--tests] [--no-prune] [--dump [TOMLFILE]] ', & + ' [--tests] [--no-prune] [--dump [FILENAME]] ', & ' new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & ' [--full|--bare][--backfill] ', & - ' update [NAME(s)] [--fetch-only] [--clean] [--dump [TOMLFILE]] ', & + ' update [NAME(s)] [--fetch-only] [--clean] [--dump [FILENAME]] ', & ' run [[--target] NAME(s)] [--profile PROF] [--flag FFLAGS] [--list] [--all] ', & ' [--example] [--runner "CMD"] [--compiler COMPILER_NAME] ', & ' [--no-prune] [-- ARGS] ', & @@ -1009,7 +1009,7 @@ subroutine set_help() ' ', & 'SYNOPSIS ', & ' fpm build [--profile PROF] [--flag FFLAGS] [--compiler COMPILER_NAME] ', & - ' [--list] [--tests] [--dump [TOMLFILE]] ', & + ' [--list] [--tests] [--dump [FILENAME]] ', & ' ', & ' fpm build --help|--version ', & ' ', & @@ -1037,7 +1037,8 @@ subroutine set_help() ' --list list candidates instead of building or running them ', & ' --tests build all tests (otherwise only if needed) ', & ' --show-model show the model and exit (do not build) ', & - ' --dump [TOMLFILE] save model representation to TOMLFILE ', & + ' --dump [FILENAME] save model representation to file. use JSON format ', & + ' if file name is *.json; use TOML format otherwise ', & ' (default file name: model.toml) ', & ' --help print this help and exit ', & ' --version print program version information and exit ', & @@ -1238,7 +1239,7 @@ subroutine set_help() ' update(1) - manage project dependencies', & '', & 'SYNOPSIS', & - ' fpm update [--fetch-only] [--clean] [--verbose] [--dump [TOMLFILE]] [NAME(s)]', & + ' fpm update [--fetch-only] [--clean] [--verbose] [--dump [FILENAME]] [NAME(s)]', & '', & 'DESCRIPTION', & ' Manage and update project dependencies. If no dependency names are', & @@ -1248,7 +1249,9 @@ subroutine set_help() ' --fetch-only Only fetch dependencies, do not update existing projects', & ' --clean Do not use previous dependency cache', & ' --verbose Show additional printout', & - ' --dump [TOMLFILE] Dump updated dependency tree to a toml file (default: fpm_dependencies.toml)', & + ' --dump [FILENAME] Dump updated dependency tree to file. use JSON format ', & + ' if file name is *.json; use TOML format otherwise ', & + ' (default file name: fpm_dependencies.toml) ', & '', & 'SEE ALSO', & ' The fpm(1) home page at https://github.com/fortran-lang/fpm', & From 7b4a153d65dd134334c03cfcb13c06b86e74e553 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 13 Apr 2023 15:43:51 +0200 Subject: [PATCH 300/799] standardize some more toml interfaces --- src/fpm/dependency.f90 | 76 ++++++++++-------------------------------- 1 file changed, 18 insertions(+), 58 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 6b700f0835..e0485a068e 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1293,46 +1293,19 @@ subroutine node_dump_to_toml(self, table, error) if (allocated(error)) return if (allocated(self%version)) then - call set_value(table, "version", self%version%s(), ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot set version in TOML table') - return - end if + call set_string(table, "version", self%version%s(), error,'dependency_node_t') + if (allocated(error)) return endif - - if (allocated(self%proj_dir)) then - call set_value(table, "proj-dir", self%proj_dir, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot set proj_dir in TOML table') - return - end if - endif - - if (allocated(self%revision)) then - call set_value(table, "revision", self%revision, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot set revision in TOML table') - return - end if - endif - - call set_value(table, "done", self%done, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot set done in TOML table') - return - end if - - call set_value(table, "update", self%update, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot set update in TOML table') - return - end if - - call set_value(table, "cached", self%cached, ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_node_t: cannot set cached in TOML table') - return - end if + call set_string(table, "proj-dir", self%proj_dir, error, 'dependency_node_t') + if (allocated(error)) return + call set_string(table, "revision", self%revision, error, 'dependency_node_t') + if (allocated(error)) return + call set_value(table, "done", self%done, error, 'dependency_node_t') + if (allocated(error)) return + call set_value(table, "update", self%update, error, 'dependency_node_t') + if (allocated(error)) return + call set_value(table, "cached", self%cached, error, 'dependency_node_t') + if (allocated(error)) return end subroutine node_dump_to_toml @@ -1514,25 +1487,12 @@ subroutine tree_load_from_toml(self, table, error) call table%get_keys(keys) - call get_value(table, "unit", self%unit, stat=ierr) - if (ierr/=toml_stat%success) then - print *, 'unit=',self%unit,' ierr=',ierr - call fatal_error(error,'dependency_tree_t: cannot get in TOML table') - return - end if - - call get_value(table, "verbosity", self%verbosity, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot get in TOML table') - return - end if - - call get_value(table, "ndep", self%ndep, stat=ierr) - if (ierr/=toml_stat%success) then - call fatal_error(error,'dependency_tree_t: cannot get in TOML table') - return - end if - + call get_value(table, "unit", self%unit, error, 'dependency_tree_t') + if (allocated(error)) return + call get_value(table, "verbosity", self%verbosity, error, 'dependency_tree_t') + if (allocated(error)) return + call get_value(table, "ndep", self%ndep, error, 'dependency_tree_t') + if (allocated(error)) return call get_value(table, "dep-dir", self%dep_dir) call get_value(table, "cache", self%cache) From a381863805c0748fe2edb52620756da76d22619c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 02:30:51 -0500 Subject: [PATCH 301/799] set testfile to readwrite --- src/fpm/toml.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 5a1508669b..bf8997fdf8 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -137,7 +137,7 @@ subroutine test_serialization(self, message, error) all_formats: do ii = 1, 2 - open(newunit=iunit,form='formatted',status='scratch') + open(newunit=iunit,form='formatted',action='readwrite',status='scratch') !> Dump to scratch file call self%dump(iunit, error, json=ii==2) From 986d079fe83c299ca2b950bf9a381df2821d027b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 02:39:45 -0500 Subject: [PATCH 302/799] do not use `scratch` units --- src/fpm/toml.f90 | 12 +++++++++--- test/fpm_test/test_package_dependencies.f90 | 6 ++++-- test/fpm_test/test_toml.f90 | 11 ++++++----- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index bf8997fdf8..16a1f26e3a 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -22,6 +22,7 @@ module fpm_toml use jonquil, only: json_serialize, json_error, json_value, json_object, json_load, & cast_to_object use iso_fortran_env, only: int64 + use fpm_filesystem, only: get_temp_filename implicit none private @@ -133,11 +134,14 @@ subroutine test_serialization(self, message, error) integer :: iunit, ii class(serializable_t), allocatable :: copy + character(len=:), allocatable :: filename character(len=4), parameter :: formats(2) = ['TOML','JSON'] all_formats: do ii = 1, 2 - open(newunit=iunit,form='formatted',action='readwrite',status='scratch') + filename = get_temp_filename() + + open(newunit=iunit,file=filename,form='formatted',action='write') !> Dump to scratch file call self%dump(iunit, error, json=ii==2) @@ -145,16 +149,18 @@ subroutine test_serialization(self, message, error) error%message = formats(ii)//': '//error%message return endif + close(iunit) !> Load from scratch file - rewind(iunit) + open(newunit=iunit,file=filename,form='formatted',action='read') allocate(copy,mold=self) call copy%load(iunit,error, json=ii==2) if (allocated(error)) then error%message = formats(ii)//': '//error%message return endif - close(iunit) + + close(iunit,status='delete') !> Check same if (.not.(self==copy)) then diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 425e124dd4..3d2cc50663 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -89,6 +89,7 @@ subroutine test_cache_dump_load(error) type(dependency_tree_t) :: deps type(dependency_config_t) :: dep + character(len=:), allocatable :: filename integer :: unit call new_dependency_tree(deps) @@ -104,7 +105,8 @@ subroutine test_cache_dump_load(error) dep%path = "fpm-tmp3-dir" call new_dependency_node(deps%dep(3), dep, proj_dir=dep%path) - open (newunit=unit, status='scratch') + filename = get_temp_filename() + open (newunit=unit, file=filename, action='readwrite', form='formatted') call deps%dump_cache(unit, error) if (.not. allocated(error)) then rewind (unit) @@ -112,7 +114,7 @@ subroutine test_cache_dump_load(error) call new_dependency_tree(deps) call resize(deps%dep, 2) call deps%load_cache(unit, error) - close (unit) + close (unit,status='delete') end if if (allocated(error)) return diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index fa17b2fea3..5280611c8d 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -1075,18 +1075,19 @@ subroutine string_to_toml(string, table) type(toml_table), allocatable, intent(out) :: table integer :: iunit + character(len=:), allocatable :: filename - ! Write - open(newunit=iunit,form='formatted',status='scratch',action='readwrite') + filename = get_temp_filename() !> Dump to scratch file + open(newunit=iunit,file=filename,form='formatted',action='write') write(iunit,*) string + close(iunit) !> Load from scratch file - rewind(iunit) + open(newunit=iunit,file=filename,form='formatted',action='read') call toml_load(table, iunit) - - close(iunit) + close(iunit,status='delete') end subroutine string_to_toml From df1128aba644e9ee8ba577ddab51fbc557cd2ce3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 02:55:45 -0500 Subject: [PATCH 303/799] CI Windows bug -> load JSON from string --- src/fpm/toml.f90 | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 16a1f26e3a..c7ac5d68c9 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -19,7 +19,9 @@ module fpm_toml & set_value, toml_parse, toml_error, new_table, add_table, add_array, & & toml_serialize, len, toml_load, toml_value use tomlf_de_parser, only: parse - use jonquil, only: json_serialize, json_error, json_value, json_object, json_load, & + use tomlf_constants, only: TOML_NEWLINE + use tomlf_utils, only: read_whole_line + use jonquil, only: json_serialize, json_error, json_value, json_object, json_loads, & cast_to_object use iso_fortran_env, only: int64 use fpm_filesystem, only: get_temp_filename @@ -266,14 +268,22 @@ subroutine load_from_unit(self, unit, error, json) type(toml_table), allocatable :: table type(toml_table), pointer :: jtable class(toml_value), allocatable :: object + character(len=:), allocatable :: unit_string logical :: is_json is_json = .false.; if (present(json)) is_json = json if (is_json) then + !> Bypass gfortran+Windows issue in Jonquil + call read_whole_unit(unit_string, unit, error) + if (allocated(error)) return + + !> Add a few spaces + unit_string = unit_string // repeat(' ',10) + !> init JSON interpreter - call json_load(object, unit, error=toml_error) + call json_loads(object, unit_string, error=toml_error) if (allocated(toml_error)) then allocate (error) call move_alloc(toml_error%message, error%message) @@ -309,6 +319,51 @@ subroutine load_from_unit(self, unit, error, json) end subroutine load_from_unit + !> Create a new instance of a lexer by reading from a unit. + !> + !> Currently, only sequential access units can be processed by this constructor. + subroutine read_whole_unit(string, iunit, error) + !> Whole file string + character(len=:), allocatable, intent(out) :: string + !> Unit to read from + integer, intent(in) :: iunit + !> Error code + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: source, line + integer, parameter :: bufsize = 512 + character(len=bufsize) :: filename, mode + integer :: stat + + inquire(unit=iunit, access=mode, name=filename) + select case(trim(mode)) + case default + stat = 1 + call fatal_error(error, "Failed to read from unit: unit is not sequential") + return + + case("sequential", "SEQUENTIAL") + allocate(character(0) :: source) + do + call read_whole_line(iunit, line, stat) + if (stat > 0) exit + source = source // line // TOML_NEWLINE + if (stat < 0) then + if (is_iostat_end(stat)) stat = 0 + exit + end if + end do + end select + + !> Pass to output + allocate(character(len=len(source)) :: string) + string(1:len(source)) = source(1:len(source)) + + if (stat /= 0) then + call fatal_error(error, "Failed to read from unit") + end if + end subroutine read_whole_unit + !> Process the configuration file to a TOML data structure subroutine read_package_file(table, manifest, error) From 946aab18a5a5dd68e0cdb433a86aa49c9e623219 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:04:57 -0500 Subject: [PATCH 304/799] temporary: output ASCII sequence --- src/fpm/toml.f90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index c7ac5d68c9..82f61b61a4 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -270,6 +270,7 @@ subroutine load_from_unit(self, unit, error, json) class(toml_value), allocatable :: object character(len=:), allocatable :: unit_string logical :: is_json + integer :: i is_json = .false.; if (present(json)) is_json = json @@ -280,7 +281,9 @@ subroutine load_from_unit(self, unit, error, json) if (allocated(error)) return !> Add a few spaces - unit_string = unit_string // repeat(' ',10) +! unit_string = unit_string // repeat(' ',10) + + print "(a,*(1x,i0))", 'input string: ',(iachar(unit_string(i:i)),i=1,len(unit_string)) !> init JSON interpreter call json_loads(object, unit_string, error=toml_error) @@ -347,7 +350,7 @@ subroutine read_whole_unit(string, iunit, error) do call read_whole_line(iunit, line, stat) if (stat > 0) exit - source = source // line // TOML_NEWLINE + source = source // line // new_line(TOML_NEWLINE) if (stat < 0) then if (is_iostat_end(stat)) stat = 0 exit From c415327d0e3427abb302051ba9c4635a71adf6a3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:13:01 -0500 Subject: [PATCH 305/799] temporary: echo compiler version --- .github/workflows/CI.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 297fe11514..dc48b801d9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -155,6 +155,7 @@ jobs: ${{ env.FPM }} run ${{ matrix.release-flags }} ${{ env.FPM }} run ${{ matrix.release-flags }} -- --version ${{ env.FPM }} run ${{ matrix.release-flags }} -- --help + gfortran --version - name: Test Fortran fpm shell: bash From 4c0a79dba0e6422bec1614e4f4f89592b8e3710f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:16:56 -0500 Subject: [PATCH 306/799] gfortran version on bootstrap --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc48b801d9..dc4fb815e6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -102,6 +102,7 @@ jobs: ${{ env.BOOTSTRAP }} run ${{ env.BOOTSTRAP }} run -- --version ${{ env.BOOTSTRAP }} run -- --help + gfortran --version - name: Test Fortran fpm (bootstrap) shell: bash @@ -155,7 +156,6 @@ jobs: ${{ env.FPM }} run ${{ matrix.release-flags }} ${{ env.FPM }} run ${{ matrix.release-flags }} -- --version ${{ env.FPM }} run ${{ matrix.release-flags }} -- --help - gfortran --version - name: Test Fortran fpm shell: bash From 2c494ed0f98eeb50c83e4e9cb81c330bbd1fd339 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:26:12 -0500 Subject: [PATCH 307/799] update mingW gfortran to 10.4.0 --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc4fb815e6..1cfa2f28bd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -77,7 +77,7 @@ jobs: Expand-Archive mingw-w64.zip echo "$pwd\mingw-w64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append env: - GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/9.4.0-9.0.0-msvcrt-r2/winlibs-x86_64-posix-seh-gcc-9.4.0-mingw-w64-9.0.0-r2.zip" + GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/10.4.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-10.4.0-mingw-w64msvcrt-10.0.0-r1.zip" # Phase 1: Bootstrap fpm with existing version - name: Install fpm From 96d0ea59490e1b05cceda71fb32b55fbe8a0a746 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:34:45 -0500 Subject: [PATCH 308/799] Update to MinGW gfortran 10.4.0 --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 297fe11514..ec085500e8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -77,7 +77,7 @@ jobs: Expand-Archive mingw-w64.zip echo "$pwd\mingw-w64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append env: - GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/9.4.0-9.0.0-msvcrt-r2/winlibs-x86_64-posix-seh-gcc-9.4.0-mingw-w64-9.0.0-r2.zip" + GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/10.4.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-10.4.0-mingw-w64msvcrt-10.0.0-r1.zip" # Phase 1: Bootstrap fpm with existing version - name: Install fpm From 0477230397a3cf9c98b59dc4ccba1349077fabad Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:50:34 -0500 Subject: [PATCH 309/799] Revert "update mingW gfortran to 10.4.0" This reverts commit 2c494ed0f98eeb50c83e4e9cb81c330bbd1fd339. --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1cfa2f28bd..dc4fb815e6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -77,7 +77,7 @@ jobs: Expand-Archive mingw-w64.zip echo "$pwd\mingw-w64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append env: - GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/10.4.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-10.4.0-mingw-w64msvcrt-10.0.0-r1.zip" + GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/9.4.0-9.0.0-msvcrt-r2/winlibs-x86_64-posix-seh-gcc-9.4.0-mingw-w64-9.0.0-r2.zip" # Phase 1: Bootstrap fpm with existing version - name: Install fpm From c01e85e0c8462a0162a83d067f62ca40d6bcf928 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:50:49 -0500 Subject: [PATCH 310/799] Revert "gfortran version on bootstrap" This reverts commit 4c0a79dba0e6422bec1614e4f4f89592b8e3710f. --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc4fb815e6..dc48b801d9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -102,7 +102,6 @@ jobs: ${{ env.BOOTSTRAP }} run ${{ env.BOOTSTRAP }} run -- --version ${{ env.BOOTSTRAP }} run -- --help - gfortran --version - name: Test Fortran fpm (bootstrap) shell: bash @@ -156,6 +155,7 @@ jobs: ${{ env.FPM }} run ${{ matrix.release-flags }} ${{ env.FPM }} run ${{ matrix.release-flags }} -- --version ${{ env.FPM }} run ${{ matrix.release-flags }} -- --help + gfortran --version - name: Test Fortran fpm shell: bash From 048a1db3a136f72ba30df71e2a39fea07e0a9c9b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:50:53 -0500 Subject: [PATCH 311/799] Revert "temporary: echo compiler version" This reverts commit c415327d0e3427abb302051ba9c4635a71adf6a3. --- .github/workflows/CI.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc48b801d9..297fe11514 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -155,7 +155,6 @@ jobs: ${{ env.FPM }} run ${{ matrix.release-flags }} ${{ env.FPM }} run ${{ matrix.release-flags }} -- --version ${{ env.FPM }} run ${{ matrix.release-flags }} -- --help - gfortran --version - name: Test Fortran fpm shell: bash From 5ed2dc4d0e56297855dda5bf1ca95a3162d7f70e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:50:57 -0500 Subject: [PATCH 312/799] Revert "temporary: output ASCII sequence" This reverts commit 946aab18a5a5dd68e0cdb433a86aa49c9e623219. --- src/fpm/toml.f90 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 82f61b61a4..c7ac5d68c9 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -270,7 +270,6 @@ subroutine load_from_unit(self, unit, error, json) class(toml_value), allocatable :: object character(len=:), allocatable :: unit_string logical :: is_json - integer :: i is_json = .false.; if (present(json)) is_json = json @@ -281,9 +280,7 @@ subroutine load_from_unit(self, unit, error, json) if (allocated(error)) return !> Add a few spaces -! unit_string = unit_string // repeat(' ',10) - - print "(a,*(1x,i0))", 'input string: ',(iachar(unit_string(i:i)),i=1,len(unit_string)) + unit_string = unit_string // repeat(' ',10) !> init JSON interpreter call json_loads(object, unit_string, error=toml_error) @@ -350,7 +347,7 @@ subroutine read_whole_unit(string, iunit, error) do call read_whole_line(iunit, line, stat) if (stat > 0) exit - source = source // line // new_line(TOML_NEWLINE) + source = source // line // TOML_NEWLINE if (stat < 0) then if (is_iostat_end(stat)) stat = 0 exit From b1497f9e36972fb50799493fc862e27454ecfeed Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:51:01 -0500 Subject: [PATCH 313/799] Revert "CI Windows bug -> load JSON from string" This reverts commit df1128aba644e9ee8ba577ddab51fbc557cd2ce3. --- src/fpm/toml.f90 | 59 ++---------------------------------------------- 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index c7ac5d68c9..16a1f26e3a 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -19,9 +19,7 @@ module fpm_toml & set_value, toml_parse, toml_error, new_table, add_table, add_array, & & toml_serialize, len, toml_load, toml_value use tomlf_de_parser, only: parse - use tomlf_constants, only: TOML_NEWLINE - use tomlf_utils, only: read_whole_line - use jonquil, only: json_serialize, json_error, json_value, json_object, json_loads, & + use jonquil, only: json_serialize, json_error, json_value, json_object, json_load, & cast_to_object use iso_fortran_env, only: int64 use fpm_filesystem, only: get_temp_filename @@ -268,22 +266,14 @@ subroutine load_from_unit(self, unit, error, json) type(toml_table), allocatable :: table type(toml_table), pointer :: jtable class(toml_value), allocatable :: object - character(len=:), allocatable :: unit_string logical :: is_json is_json = .false.; if (present(json)) is_json = json if (is_json) then - !> Bypass gfortran+Windows issue in Jonquil - call read_whole_unit(unit_string, unit, error) - if (allocated(error)) return - - !> Add a few spaces - unit_string = unit_string // repeat(' ',10) - !> init JSON interpreter - call json_loads(object, unit_string, error=toml_error) + call json_load(object, unit, error=toml_error) if (allocated(toml_error)) then allocate (error) call move_alloc(toml_error%message, error%message) @@ -319,51 +309,6 @@ subroutine load_from_unit(self, unit, error, json) end subroutine load_from_unit - !> Create a new instance of a lexer by reading from a unit. - !> - !> Currently, only sequential access units can be processed by this constructor. - subroutine read_whole_unit(string, iunit, error) - !> Whole file string - character(len=:), allocatable, intent(out) :: string - !> Unit to read from - integer, intent(in) :: iunit - !> Error code - type(error_t), allocatable, intent(out) :: error - - character(len=:), allocatable :: source, line - integer, parameter :: bufsize = 512 - character(len=bufsize) :: filename, mode - integer :: stat - - inquire(unit=iunit, access=mode, name=filename) - select case(trim(mode)) - case default - stat = 1 - call fatal_error(error, "Failed to read from unit: unit is not sequential") - return - - case("sequential", "SEQUENTIAL") - allocate(character(0) :: source) - do - call read_whole_line(iunit, line, stat) - if (stat > 0) exit - source = source // line // TOML_NEWLINE - if (stat < 0) then - if (is_iostat_end(stat)) stat = 0 - exit - end if - end do - end select - - !> Pass to output - allocate(character(len=len(source)) :: string) - string(1:len(source)) = source(1:len(source)) - - if (stat /= 0) then - call fatal_error(error, "Failed to read from unit") - end if - end subroutine read_whole_unit - !> Process the configuration file to a TOML data structure subroutine read_package_file(table, manifest, error) From 47e8f1b5c2a5bb9c6260a62faddffc6e69be4418 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 14 Apr 2023 03:51:04 -0500 Subject: [PATCH 314/799] Revert "do not use `scratch` units" This reverts commit 986d079fe83c299ca2b950bf9a381df2821d027b. --- src/fpm/toml.f90 | 12 +++--------- test/fpm_test/test_package_dependencies.f90 | 6 ++---- test/fpm_test/test_toml.f90 | 11 +++++------ 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index 16a1f26e3a..bf8997fdf8 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -22,7 +22,6 @@ module fpm_toml use jonquil, only: json_serialize, json_error, json_value, json_object, json_load, & cast_to_object use iso_fortran_env, only: int64 - use fpm_filesystem, only: get_temp_filename implicit none private @@ -134,14 +133,11 @@ subroutine test_serialization(self, message, error) integer :: iunit, ii class(serializable_t), allocatable :: copy - character(len=:), allocatable :: filename character(len=4), parameter :: formats(2) = ['TOML','JSON'] all_formats: do ii = 1, 2 - filename = get_temp_filename() - - open(newunit=iunit,file=filename,form='formatted',action='write') + open(newunit=iunit,form='formatted',action='readwrite',status='scratch') !> Dump to scratch file call self%dump(iunit, error, json=ii==2) @@ -149,18 +145,16 @@ subroutine test_serialization(self, message, error) error%message = formats(ii)//': '//error%message return endif - close(iunit) !> Load from scratch file - open(newunit=iunit,file=filename,form='formatted',action='read') + rewind(iunit) allocate(copy,mold=self) call copy%load(iunit,error, json=ii==2) if (allocated(error)) then error%message = formats(ii)//': '//error%message return endif - - close(iunit,status='delete') + close(iunit) !> Check same if (.not.(self==copy)) then diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 3d2cc50663..425e124dd4 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -89,7 +89,6 @@ subroutine test_cache_dump_load(error) type(dependency_tree_t) :: deps type(dependency_config_t) :: dep - character(len=:), allocatable :: filename integer :: unit call new_dependency_tree(deps) @@ -105,8 +104,7 @@ subroutine test_cache_dump_load(error) dep%path = "fpm-tmp3-dir" call new_dependency_node(deps%dep(3), dep, proj_dir=dep%path) - filename = get_temp_filename() - open (newunit=unit, file=filename, action='readwrite', form='formatted') + open (newunit=unit, status='scratch') call deps%dump_cache(unit, error) if (.not. allocated(error)) then rewind (unit) @@ -114,7 +112,7 @@ subroutine test_cache_dump_load(error) call new_dependency_tree(deps) call resize(deps%dep, 2) call deps%load_cache(unit, error) - close (unit,status='delete') + close (unit) end if if (allocated(error)) return diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 5280611c8d..fa17b2fea3 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -1075,19 +1075,18 @@ subroutine string_to_toml(string, table) type(toml_table), allocatable, intent(out) :: table integer :: iunit - character(len=:), allocatable :: filename - filename = get_temp_filename() + ! Write + open(newunit=iunit,form='formatted',status='scratch',action='readwrite') !> Dump to scratch file - open(newunit=iunit,file=filename,form='formatted',action='write') write(iunit,*) string - close(iunit) !> Load from scratch file - open(newunit=iunit,file=filename,form='formatted',action='read') + rewind(iunit) call toml_load(table, iunit) - close(iunit,status='delete') + + close(iunit) end subroutine string_to_toml From 39ae27d6ad94d35e79e97da07b88d2a4e5ef1939 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 01:38:23 -0500 Subject: [PATCH 315/799] fix merged CI Script --- ci/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 4a6645c6d3..e937b421b0 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -219,6 +219,7 @@ test $EXIT_CODE -eq 0 EXIT_CODE=0 "$fpm" run || EXIT_CODE=$? test $EXIT_CODE -eq 0 +popd # test dependency priority pushd dependency_priority @@ -243,7 +244,6 @@ if [[ -z "$(grep Update update.log)" ]]; then echo "No updated dependencies after 'fpm update --clean'"; exit 1; fi - popd # Cleanup From 82e268dd90119ef469e74466eb57f85cc191c776 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 04:14:00 -0500 Subject: [PATCH 316/799] MS-MPI: search for paths, resolve paths with spaces to DOS --- src/fpm_meta.f90 | 243 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 199 insertions(+), 44 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index a9800d5f5e..f56df543da 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -21,8 +21,9 @@ module fpm_meta use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix -use fpm_filesystem, only: run, get_temp_filename, getline +use fpm_filesystem, only: run, get_temp_filename, getline, exists use fpm_versioning, only: version_t, new_version +use fpm_os, only: get_absolute_path use iso_fortran_env, only: stdout => output_unit use regex_module, only: regex @@ -79,6 +80,9 @@ module fpm_meta integer, parameter :: MPI_TYPE_INTEL = 3 integer, parameter :: MPI_TYPE_MSMPI = 4 +!> Debugging information +logical, parameter, private :: verbose = .true. + contains !> Clean the metapackage structure @@ -338,17 +342,19 @@ end subroutine resolve_metapackage_model !> Initialize MPI metapackage for the current system subroutine init_mpi(this,compiler,error) + use iso_fortran_env, only: compiler_version,compiler_options class(metapackage_t), intent(inout) :: this type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - logical, parameter :: verbose = .true. + type(string_t), allocatable :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) type(string_t) :: output - type(version_t) :: version character(256) :: msg_out character(len=:), allocatable :: tokens(:) - integer :: ifort,ic,icpp,i + integer :: mpif90,ic,icpp,i + logical :: wcfit,found + !> Cleanup call destroy(this) @@ -357,60 +363,209 @@ subroutine init_mpi(this,compiler,error) call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) if (verbose) print 1, size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) - if (size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)<=0) then - call fatal_error(error,"cannot find MPI wrappers for "//compiler%name()//" compiler") - return + wcfit = wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) + + if (allocated(error) .or. .not.wcfit) then + + !> No wrapper compiler fit. Are we on Windows? use MSMPI-specific search + found = msmpi_init(this) + + !> All attempts failed + if (.not.found) then + call fatal_error(error,"cannot find MPI wrappers or libraries for "//compiler%name()//" compiler") + return + endif + + else + + !> Initialize MPI package from wrapper command + call init_mpi_from_wrapper(this,compiler,fort_wrappers(mpif90),error) + if (allocated(error)) return + end if - !> Return an MPI wrapper that matches the current compiler - ifort = mpi_compiler_match(fort_wrappers,compiler,error) - if (allocated(error)) return + 1 format('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0) - !C, C++ not available yet - !ic = mpi_compiler_match(c_wrappers,compiler,error) - !icpp = mpi_compiler_match(cpp_wrappers,compiler,error) +end subroutine init_mpi - !> Build MPI dependency - if (ifort>0) then +!> Check if we're on a 64-bit environment +!> Accept answer from https://stackoverflow.com/questions/49141093/get-system-information-with-fortran +logical function is_64bit_environment() + use iso_c_binding, only: c_intptr_t + integer, parameter :: nbits = bit_size(0_c_intptr_t) + is_64bit_environment = nbits==64 +end function is_64bit_environment + +!> Check if there is a wrapper-compiler fit +logical function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) + type(string_t), allocatable, intent(in) :: fort_wrappers(:),c_wrappers(:),cpp_wrappers(:) + type(compiler_t), intent(in) :: compiler + type(error_t), allocatable, intent(out) :: error - ! Get linking flags - this%link_flags = mpi_wrapper_query(fort_wrappers(ifort),'link',verbose,error) - if (allocated(error)) return - this%has_link_flags = len_trim(this%link_flags)>0 + logical :: has_wrappers + integer :: mpif90 - ! Add heading space - this%link_flags = string_t(' '//this%link_flags%s) + wrapper_compiler_fit = .false. - ! Get build flags - this%flags = mpi_wrapper_query(fort_wrappers(ifort),'flags',verbose,error) - if (allocated(error)) return - this%has_build_flags = len_trim(this%flags)>0 + !> Were any wrappers found? + has_wrappers = size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)>0 - ! Add heading space - this%flags = string_t(' '//this%flags%s) + if (has_wrappers) then - ! Get library version - version = mpi_version_get(fort_wrappers(ifort),error) - if (allocated(error)) then - return - else - allocate(this%version,source=version) - end if + !> Find an MPI wrapper that matches the current compiler + mpif90 = mpi_compiler_match(fort_wrappers,compiler,error) + if (allocated(error)) return + + !> Was a valid wrapper found? + wrapper_compiler_fit = mpif90>0 + + endif + +end function wrapper_compiler_fit + +!> Check if a local MS-MPI SDK build is found +logical function msmpi_init(this) result(found) + class(metapackage_t), intent(inout) :: this + + character(len=:), allocatable :: incdir,libdir,post,reall + type(error_t), allocatable :: error + + + !> Default: not found + found = .false. + + if (get_os_type()==OS_WINDOWS) then + + !> Find include and library directories + incdir = get_env('MSMPI_INC') + if (is_64bit_environment()) then + libdir = get_env('MSMPI_LIB64') + post = 'x64' + else + libdir = get_env('MSMPI_LIB32') + post = 'x86' + end if + + if (verbose) print 1, 'include',incdir,exists(incdir) + if (verbose) print 1, 'library',libdir,exists(libdir) + + ! Both directories need be defined and existent + if (len_trim(incdir)<=0 .or. len_trim(libdir)<=0) return + if (.not.exists(incdir) .or. .not.exists(libdir)) return + + ! Init ms-mpi + call destroy(this) + + this%has_link_flags = .true. + this%link_flags = string_t(' -l'//get_dos_path(libdir//'msmpi')// & + ' -l'//get_dos_path(libdir//'msmpifec')) ! fortran-only + + this%has_include_dirs = .true. + this%incl_dirs = [string_t(get_dos_path(incdir)), & + string_t(get_dos_path(incdir//post))] + + call get_absolute_path(libdir//'msmpi.lib', reall, error) + if (allocated(error)) stop 'cannot get realpath '//error%message + print *, 'real pach= ',reall + + found = .true. else - ! None of the available wrappers matched the current Fortran compiler - write(msg_out,1) size(fort_wrappers),compiler%fc - call fatal_error(error,trim(msg_out)) - return + !> Not on Windows + found = .false. - endif + end if + 1 format('MSMSPI ',a,' directory: PATH=',a,' EXISTS=',l1) - 1 format('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0) - 2 format(' None out of ',i0,' valid MPI wrappers matches compiler ',a) +end function msmpi_init -end subroutine init_mpi +!> Ensure a windows path is converted to a DOS path if it contains spaces +function get_dos_path(path) + character(len=*), intent(in) :: path + character(len=:), allocatable :: get_dos_path + + character(:), allocatable :: redirect,screen_output,line + integer :: stat,cmdstat,iunit + + ! Trim path first + get_dos_path = trim(path) + + !> No need to convert if there are no spaces + if (scan(get_dos_path,' ')<=0) return + + + redirect = get_temp_filename() + call execute_command_line('cmd /c for %A in ("'//path//'") do @echo %~sA >'//redirect//' 2>&1',& + exitstat=stat,cmdstat=cmdstat) + + !> Read screen output + if (cmdstat==0) then + + allocate(character(len=0) :: screen_output) + open(newunit=iunit,file=redirect,status='old',iostat=stat) + if (stat == 0)then + do + call getline(iunit, line, stat) + if (stat /= 0) exit + screen_output = screen_output//line//' ' + end do + + ! Close and delete file + close(iunit,status='delete') + + else + call fpm_stop(1,'cannot read temporary file from successful DOS path evaluation') + endif + + else + + call fpm_stop(1,'cannot convert windows path to DOS path') + + end if + + get_dos_path = trim(adjustl(screen_output)) + +end function get_dos_path + +!> Initialize an MPI metapackage from a valid wrapper command ('mpif90', etc...) +subroutine init_mpi_from_wrapper(this,compiler,fort_wrapper,error) + class(metapackage_t), intent(inout) :: this + type(compiler_t), intent(in) :: compiler + type(string_t), intent(in) :: fort_wrapper + type(error_t), allocatable, intent(out) :: error + + type(version_t) :: version + + ! Cleanup structure + call destroy(this) + + ! Get linking flags + this%link_flags = mpi_wrapper_query(fort_wrapper,'link',verbose,error) + if (allocated(error)) return + this%has_link_flags = len_trim(this%link_flags)>0 + + ! Add heading space + this%link_flags = string_t(' '//this%link_flags%s) + + ! Get build flags + this%flags = mpi_wrapper_query(fort_wrapper,'flags',verbose,error) + if (allocated(error)) return + this%has_build_flags = len_trim(this%flags)>0 + + ! Add heading space + this%flags = string_t(' '//this%flags%s) + + ! Get library version + version = mpi_version_get(fort_wrapper,error) + if (allocated(error)) then + return + else + allocate(this%version,source=version) + end if + +end subroutine init_mpi_from_wrapper !> Match one of the available compiler wrappers with the current compiler integer function mpi_compiler_match(wrappers,compiler,error) @@ -427,7 +582,7 @@ integer function mpi_compiler_match(wrappers,compiler,error) do i=1,size(wrappers) - screen = mpi_wrapper_query(wrappers(i),'compiler',.false.,error) + screen = mpi_wrapper_query(wrappers(i),'compiler',verbose=.false.,error=error) if (allocated(error)) return ! Build compiler type @@ -607,7 +762,7 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp screen_output%s = screen_output%s//new_line('a')//line - write(*,'(A)') trim(line) + if (verbose) write(*,'(A)') trim(line) end do ! Close and delete file From d5ef9d6315b2762294b025269a10103c276dc97c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 04:23:51 -0500 Subject: [PATCH 317/799] fix MS-MPI paths --- src/fpm_meta.f90 | 57 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index f56df543da..3e68f2e40e 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -457,17 +457,13 @@ logical function msmpi_init(this) result(found) call destroy(this) this%has_link_flags = .true. - this%link_flags = string_t(' -l'//get_dos_path(libdir//'msmpi')// & - ' -l'//get_dos_path(libdir//'msmpifec')) ! fortran-only + this%link_flags = string_t(' -l'//get_dos_path(join_path(libdir,'msmpi'))// & + ' -l'//get_dos_path(join_path(libdir,'msmpifec'))) ! fortran-only this%has_include_dirs = .true. this%incl_dirs = [string_t(get_dos_path(incdir)), & string_t(get_dos_path(incdir//post))] - call get_absolute_path(libdir//'msmpi.lib', reall, error) - if (allocated(error)) stop 'cannot get realpath '//error%message - print *, 'real pach= ',reall - found = .true. else @@ -487,46 +483,51 @@ function get_dos_path(path) character(len=:), allocatable :: get_dos_path character(:), allocatable :: redirect,screen_output,line - integer :: stat,cmdstat,iunit + integer :: stat,cmdstat,iunit,last ! Trim path first get_dos_path = trim(path) !> No need to convert if there are no spaces - if (scan(get_dos_path,' ')<=0) return + has_spaces: if (scan(get_dos_path,' ')>0) then + redirect = get_temp_filename() + call execute_command_line('cmd /c for %A in ("'//path//'") do @echo %~sA >'//redirect//' 2>&1',& + exitstat=stat,cmdstat=cmdstat) - redirect = get_temp_filename() - call execute_command_line('cmd /c for %A in ("'//path//'") do @echo %~sA >'//redirect//' 2>&1',& - exitstat=stat,cmdstat=cmdstat) + !> Read screen output + if (cmdstat==0) then - !> Read screen output - if (cmdstat==0) then + allocate(character(len=0) :: screen_output) + open(newunit=iunit,file=redirect,status='old',iostat=stat) + if (stat == 0)then + do + call getline(iunit, line, stat) + if (stat /= 0) exit + screen_output = screen_output//line//' ' + end do - allocate(character(len=0) :: screen_output) - open(newunit=iunit,file=redirect,status='old',iostat=stat) - if (stat == 0)then - do - call getline(iunit, line, stat) - if (stat /= 0) exit - screen_output = screen_output//line//' ' - end do + ! Close and delete file + close(iunit,status='delete') - ! Close and delete file - close(iunit,status='delete') + else + call fpm_stop(1,'cannot read temporary file from successful DOS path evaluation') + endif else - call fpm_stop(1,'cannot read temporary file from successful DOS path evaluation') - endif - else + call fpm_stop(1,'cannot convert windows path to DOS path') - call fpm_stop(1,'cannot convert windows path to DOS path') + end if - end if + endif has_spaces + !> Ensure there are no trailing slashes get_dos_path = trim(adjustl(screen_output)) + last = len_trim(get_dos_path) + if (last>1 .and. get_dos_path(last:last)=='/' .or. get_dos_path(last:last)=='\') get_dos_path = get_dos_path(1:last-1) + end function get_dos_path !> Initialize an MPI metapackage from a valid wrapper command ('mpif90', etc...) From f5125e933e6891933c8f92354356923edebc1597 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 04:59:34 -0500 Subject: [PATCH 318/799] allow invalid BOZ in mpif.h with gfortran --- src/fpm_meta.f90 | 114 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 15 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 3e68f2e40e..11d493a65a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -342,7 +342,6 @@ end subroutine resolve_metapackage_model !> Initialize MPI metapackage for the current system subroutine init_mpi(this,compiler,error) - use iso_fortran_env, only: compiler_version,compiler_options class(metapackage_t), intent(inout) :: this type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error @@ -368,7 +367,8 @@ subroutine init_mpi(this,compiler,error) if (allocated(error) .or. .not.wcfit) then !> No wrapper compiler fit. Are we on Windows? use MSMPI-specific search - found = msmpi_init(this) + found = msmpi_init(this,compiler,error) + if (allocated(error)) return !> All attempts failed if (.not.found) then @@ -424,12 +424,13 @@ logical function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,comp end function wrapper_compiler_fit !> Check if a local MS-MPI SDK build is found -logical function msmpi_init(this) result(found) +logical function msmpi_init(this,compiler,error) result(found) class(metapackage_t), intent(inout) :: this + type(compiler_t), intent(in) :: compiler + type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: incdir,libdir,post,reall - type(error_t), allocatable :: error - + type(version_t) :: ver,ver10 !> Default: not found found = .false. @@ -457,15 +458,36 @@ logical function msmpi_init(this) result(found) call destroy(this) this%has_link_flags = .true. - this%link_flags = string_t(' -l'//get_dos_path(join_path(libdir,'msmpi'))// & - ' -l'//get_dos_path(join_path(libdir,'msmpifec'))) ! fortran-only + this%link_flags = string_t(' -l'//get_dos_path(join_path(libdir,'msmpi'),error)// & + ' -l'//get_dos_path(join_path(libdir,'msmpifec'),error)) ! fortran-only + if (allocated(error)) return this%has_include_dirs = .true. - this%incl_dirs = [string_t(get_dos_path(incdir)), & - string_t(get_dos_path(incdir//post))] + this%incl_dirs = [string_t(get_dos_path(incdir,error)), & + string_t(get_dos_path(incdir//post,error))] + if (allocated(error)) return found = .true. + ! gfortran>=10 is incompatible with the old-style mpif.h MS-MPI headers. + ! If so, add flags to allow old-style BOZ constants in mpif.h + + allow_BOZ: if (compiler%id==id_gcc) then + ver = compiler_get_version(compiler,error) + if (allocated(error)) return + + call new_version(ver10,'10.0.0',error) + if (allocated(error)) return + + if (ver>=ver10) then + this%has_build_flags = .true. + this%flags = string_t(' -fallow-invalid-boz') + + end if + + endif allow_BOZ + + else !> Not on Windows @@ -477,9 +499,68 @@ logical function msmpi_init(this) result(found) end function msmpi_init +!> Return compiler version +type(version_t) function compiler_get_version(self,error) + type(compiler_t), intent(in) :: self + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: tmp_file,screen_output,line + integer :: stat,iunit,ire,length + + select case (self%id) + case (id_gcc) + + tmp_file = get_temp_filename() + + call run(self%fc // " --version ", echo=self%echo, verbose=self%verbose, redirect=tmp_file, exitstat=stat) + if (stat/=0) then + call fatal_error(error,'compiler_get_version failed for '//self%fc) + return + end if + + allocate(character(len=0) :: screen_output) + open(newunit=iunit,file=tmp_file,status='old',iostat=stat) + if (stat == 0)then + + do + call getline(iunit, line, stat) + if (stat /= 0) exit + screen_output = screen_output//' '//line//' ' + end do + + ! Close and delete file + close(iunit,status='delete') + + else + call fatal_error(error,'cannot read temporary file from successful compiler_get_version') + return + endif + + ! Extract version + ire = regex(screen_output,'\d+.\d+.\d+',length=length) + + if (ire>0 .and. length>0) then + ! Parse version into the object (this should always work) + screen_output = screen_output(ire:ire+length-1) + else + call syntax_error(error,'cannot retrieve '//self%fc//' compiler version.') + return + end if + + ! Wrap to object + call new_version(compiler_get_version,screen_output,error) + + case default + call fatal_error(error,'compiler_get_version not yet implemented for compiler '//self%fc) + return + end select + +end function compiler_get_version + !> Ensure a windows path is converted to a DOS path if it contains spaces -function get_dos_path(path) +function get_dos_path(path,error) character(len=*), intent(in) :: path + type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: get_dos_path character(:), allocatable :: redirect,screen_output,line @@ -496,11 +577,12 @@ function get_dos_path(path) exitstat=stat,cmdstat=cmdstat) !> Read screen output - if (cmdstat==0) then + command_OK: if (cmdstat==0 .and. stat==0) then allocate(character(len=0) :: screen_output) open(newunit=iunit,file=redirect,status='old',iostat=stat) if (stat == 0)then + do call getline(iunit, line, stat) if (stat /= 0) exit @@ -511,14 +593,16 @@ function get_dos_path(path) close(iunit,status='delete') else - call fpm_stop(1,'cannot read temporary file from successful DOS path evaluation') + call fatal_error(error,'cannot read temporary file from successful DOS path evaluation') + return endif - else + else command_OK - call fpm_stop(1,'cannot convert windows path to DOS path') + call fatal_error(error,'unsuccessful Windows->DOS path command') + return - end if + end if command_OK endif has_spaces From b354bc3adb5b6f2ca0cd6e57b196fedfd440543a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 04:59:38 -0500 Subject: [PATCH 319/799] typo --- example_packages/metapackage_mpi/app/main.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_packages/metapackage_mpi/app/main.f90 b/example_packages/metapackage_mpi/app/main.f90 index f3c3bde606..c8569f4ed3 100644 --- a/example_packages/metapackage_mpi/app/main.f90 +++ b/example_packages/metapackage_mpi/app/main.f90 @@ -18,7 +18,7 @@ program with_mpi call MPI_Comm_rank(MPI_COMM_WORLD, cpuid, ierror) if (ierror/=0) stop RANK_ERROR - print "('Hello, mpi world from rank ',i0,' of ',i0,'!')", cpuid+1,ncpu + print "('Hello, mpi world from rank ',i0,' of ',i0,'!')", cpuid+1,ncpus ! Finalize MPI environment. call MPI_FINALIZE(ierror) From c3bc23c8833120fbf75c441253192652687db350 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 08:37:24 -0500 Subject: [PATCH 320/799] allow implicit typing in example package --- example_packages/metapackage_mpi/fpm.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example_packages/metapackage_mpi/fpm.toml b/example_packages/metapackage_mpi/fpm.toml index 9deea93520..933e9568cc 100644 --- a/example_packages/metapackage_mpi/fpm.toml +++ b/example_packages/metapackage_mpi/fpm.toml @@ -5,6 +5,10 @@ author = "Federico Perini" maintainer = "federico.perini@hello.world" copyright = "Copyright 2023, Federico Perini and the fpm maintainers" +[fortran] +implicit-external = true +implicit-typing = true + [build] auto-executables = true From 9eaf1c807857bff5a455f9157fff7ab4b4c1f19b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 08:49:45 -0500 Subject: [PATCH 321/799] split in MSYS2 / non-MSYS2 cases --- example_packages/metapackage_mpi/app/main.f90 | 2 + src/fpm_meta.f90 | 236 +++++++++++++++--- 2 files changed, 203 insertions(+), 35 deletions(-) diff --git a/example_packages/metapackage_mpi/app/main.f90 b/example_packages/metapackage_mpi/app/main.f90 index c8569f4ed3..8119ac21da 100644 --- a/example_packages/metapackage_mpi/app/main.f90 +++ b/example_packages/metapackage_mpi/app/main.f90 @@ -1,4 +1,5 @@ program with_mpi + implicit none include 'mpif.h' @@ -6,6 +7,7 @@ program with_mpi integer, parameter :: RANK_ERROR = 2 integer :: ierror,ncpus,cpuid + ! Initialize MPI argument call MPI_INIT(ierror); diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 11d493a65a..2c69a2add0 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -21,7 +21,7 @@ module fpm_meta use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix -use fpm_filesystem, only: run, get_temp_filename, getline, exists +use fpm_filesystem, only: run, get_temp_filename, getline, exists, canon_path, is_dir use fpm_versioning, only: version_t, new_version use fpm_os, only: get_absolute_path use iso_fortran_env, only: stdout => output_unit @@ -51,6 +51,9 @@ module fpm_meta type(string_t), allocatable :: incl_dirs(:) type(string_t), allocatable :: link_libs(:) + !> Special fortran features + type(fortran_features_t), allocatable :: fortran + !> List of Development dependency meta data. !> Metapackage dependencies are never exported from the model type(dependency_config_t), allocatable :: dependency(:) @@ -96,6 +99,7 @@ elemental subroutine destroy(this) this%has_include_dirs = .false. this%has_dependencies = .false. + if (allocated(this%fortran)) deallocate(this%fortran) if (allocated(this%version)) deallocate(this%version) if (allocated(this%flags%s)) deallocate(this%flags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) @@ -234,7 +238,7 @@ subroutine resolve_model(self,model,error) model%include_dirs = [model%include_dirs,self%incl_dirs] end if - ! Dependencies are resolved in the package config + end subroutine resolve_model @@ -243,16 +247,48 @@ subroutine resolve_package_config(self,package,error) type(package_config_t), intent(inout) :: package type(error_t), allocatable, intent(out) :: error - ! All metapackage dependencies are added as full dependencies, - ! as upstream projects will not otherwise compile without them + ! All metapackage dependencies are added as dev-dependencies, + ! as they may change if built upstream if (self%has_dependencies) then - if (allocated(package%dependency)) then - package%dependency = [package%dependency,self%dependency] + if (allocated(package%dev_dependency)) then + package%dev_dependency = [package%dev_dependency,self%dependency] else - package%dependency = self%dependency + package%dev_dependency = self%dependency + end if + end if + + ! Check if there are any special fortran requests which the package does not comply to + if (allocated(self%fortran)) then + + if (self%fortran%implicit_external.neqv.package%fortran%implicit_external) then + call fatal_error(error,'metapackage fortran error: metapackage '// & + dn(self%fortran%implicit_external)//' require implicit-external, main package '//& + dn(package%fortran%implicit_external)) + return + end if + + if (self%fortran%implicit_typing.neqv.package%fortran%implicit_typing) then + call fatal_error(error,'metapackage fortran error: metapackage '// & + dn(self%fortran%implicit_external)//' require implicit-typing, main package '//& + dn(package%fortran%implicit_external)) + return end if + end if + contains + + pure function dn(bool) + logical, intent(in) :: bool + character(len=:), allocatable :: dn + if (bool) then + dn = "does" + else + dn = "does not" + end if + end function dn + + end subroutine resolve_package_config ! Add named metapackage dependency to the model @@ -290,6 +326,14 @@ subroutine add_metapackage_config(package,compiler,name,error) call meta%resolve(package,error) if (allocated(error)) return + ! Temporary + if (name=="mpi") then + + + + + end if + end subroutine add_metapackage_config !> Resolve all metapackages into the package config @@ -329,9 +373,6 @@ subroutine resolve_metapackage_model(model,package,error) ! MPI if (package%meta%mpi) then - - print *, 'resolving MPI...' - call add_metapackage_model(model,"mpi",error) if (allocated(error)) return call add_metapackage_config(package,model%compiler,"mpi",error) @@ -429,15 +470,25 @@ logical function msmpi_init(this,compiler,error) result(found) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: incdir,libdir,post,reall + character(len=:), allocatable :: incdir,libdir,post,reall,msysdir type(version_t) :: ver,ver10 + type(string_t) :: path + logical :: msys2 !> Default: not found found = .false. if (get_os_type()==OS_WINDOWS) then - !> Find include and library directories + ! to run MSMPI on Windows, + is_minGW: if (compiler%id==id_gcc) then + + call compiler_get_version(compiler,ver,msys2,error) + if (allocated(error)) return + + endif is_minGW + + !> Find include and library directories of the MS-MPI SDK incdir = get_env('MSMPI_INC') if (is_64bit_environment()) then libdir = get_env('MSMPI_LIB64') @@ -454,27 +505,12 @@ logical function msmpi_init(this,compiler,error) result(found) if (len_trim(incdir)<=0 .or. len_trim(libdir)<=0) return if (.not.exists(incdir) .or. .not.exists(libdir)) return - ! Init ms-mpi - call destroy(this) - - this%has_link_flags = .true. - this%link_flags = string_t(' -l'//get_dos_path(join_path(libdir,'msmpi'),error)// & - ' -l'//get_dos_path(join_path(libdir,'msmpifec'),error)) ! fortran-only - if (allocated(error)) return - - this%has_include_dirs = .true. - this%incl_dirs = [string_t(get_dos_path(incdir,error)), & - string_t(get_dos_path(incdir//post,error))] - if (allocated(error)) return - + ! Success! found = .true. ! gfortran>=10 is incompatible with the old-style mpif.h MS-MPI headers. ! If so, add flags to allow old-style BOZ constants in mpif.h - allow_BOZ: if (compiler%id==id_gcc) then - ver = compiler_get_version(compiler,error) - if (allocated(error)) return call new_version(ver10,'10.0.0',error) if (allocated(error)) return @@ -482,11 +518,64 @@ logical function msmpi_init(this,compiler,error) result(found) if (ver>=ver10) then this%has_build_flags = .true. this%flags = string_t(' -fallow-invalid-boz') - end if endif allow_BOZ + ! Init ms-mpi + call destroy(this) + + ! MSYS2 provides a pre-built static msmpi.dll.a library. Use that if possible + use_prebuilt: if (msys2) then + + call compiler_get_path(compiler,path,error) + if (allocated(error)) return + + print *, 'compiler path: '//path%s + stop + + ! Add dir path + this%has_link_flags = .true. + !this%link_flags = string_t(' -L'//get_dos_path(libdir,error)) + this%link_flags = string_t(' -LC:\msys64\mingw64\lib') + + this%has_link_libraries = .true. + this%link_libs = [string_t('msmpi.dll')] + !this%link_libs = [string_t('msmpi'),string_t('msmpifec'),string_t('msmpifmc')] + + if (allocated(error)) return + + this%has_include_dirs = .true. + this%incl_dirs = [string_t(get_dos_path(incdir,error)), & + string_t(get_dos_path(incdir//post,error))] + if (allocated(error)) return + + else + + call fatal_error(error,'MS-MPI cannot work with non-MSYS2 GNU compilers yet') + return + + ! Add dir path + this%has_link_flags = .true. + this%link_flags = string_t(' -L'//get_dos_path(libdir,error)) + + this%has_link_libraries = .true. + this%link_libs = [string_t('msmpi'),string_t('msmpifec'),string_t('msmpifmc')] + + if (allocated(error)) return + + this%has_include_dirs = .true. + this%incl_dirs = [string_t(get_dos_path(incdir,error)), & + string_t(get_dos_path(incdir//post,error))] + if (allocated(error)) return + + + end if use_prebuilt + + !> Request no Fortran implicit typing + allocate(this%fortran) + this%fortran%implicit_typing = .true. + this%fortran%implicit_external = .true. else @@ -499,14 +588,90 @@ logical function msmpi_init(this,compiler,error) result(found) end function msmpi_init +!> Return compiler path +subroutine compiler_get_path(self,path,error) + type(compiler_t), intent(in) :: self + type(string_t), intent(out) :: path + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: tmp_file,screen_output,line,fullpath + integer :: stat,iunit,ire,length + + tmp_file = get_temp_filename() + + if (get_os_type()==OS_WINDOWS) then + call run("where "//self%fc, echo=self%echo, verbose=self%verbose, redirect=tmp_file, exitstat=stat) + else + call run("which "//self%fc, echo=self%echo, verbose=self%verbose, redirect=tmp_file, exitstat=stat) + end if + if (stat/=0) then + call fatal_error(error,'compiler_get_path failed for '//self%fc) + return + end if + + ! Only read first instance (first line) + allocate(character(len=0) :: screen_output) + open(newunit=iunit,file=tmp_file,status='old',iostat=stat) + if (stat == 0)then + do + call getline(iunit, line, stat) + if (stat /= 0) exit + if (len(screen_output)>0) then + screen_output = screen_output//new_line('a')//line + else + screen_output = line + endif + end do + ! Close and delete file + close(iunit,status='delete') + else + call fatal_error(error,'cannot read temporary file from successful compiler_get_path') + return + endif + + ! Only use the first instance + length = index(screen_output,new_line('a')) + multiline: if (length>1) then + fullpath = screen_output(1:length-1) + else + fullpath = screen_output + endif multiline + if (len_trim(fullpath)<1) then + call fatal_error(error,'no paths found to the current compiler ('//self%fc//')') + return + end if + + ! Extract path only + length = index(fullpath,self%fc,BACK=.true.) + if (length<=0) then + call fatal_error(error,'full path to the current compiler ('//self%fc//') does not include compiler name') + return + elseif (length==1) then + ! Compiler is in the current folder + call get_absolute_path('.',path%s,error) + else + path%s = canon_path(fullpath(1:length-1)) + end if + + if (.not.is_dir(path%s)) then + call fatal_error(error,'full path to the current compiler ('//self%fc//') is not a directory') + return + end if + +end subroutine compiler_get_path + !> Return compiler version -type(version_t) function compiler_get_version(self,error) +subroutine compiler_get_version(self,version,is_msys2,error) type(compiler_t), intent(in) :: self + type(version_t), intent(out) :: version + logical, intent(out) :: is_msys2 type(error_t), allocatable, intent(out) :: error character(:), allocatable :: tmp_file,screen_output,line integer :: stat,iunit,ire,length + is_msys2 = .false. + select case (self%id) case (id_gcc) @@ -521,21 +686,21 @@ type(version_t) function compiler_get_version(self,error) allocate(character(len=0) :: screen_output) open(newunit=iunit,file=tmp_file,status='old',iostat=stat) if (stat == 0)then - do call getline(iunit, line, stat) if (stat /= 0) exit screen_output = screen_output//' '//line//' ' end do - ! Close and delete file close(iunit,status='delete') - else call fatal_error(error,'cannot read temporary file from successful compiler_get_version') return endif + ! Check if this gcc is from the MSYS2 project + is_msys2 = index(screen_output,'MSYS2')>0 + ! Extract version ire = regex(screen_output,'\d+.\d+.\d+',length=length) @@ -548,14 +713,15 @@ type(version_t) function compiler_get_version(self,error) end if ! Wrap to object - call new_version(compiler_get_version,screen_output,error) + call new_version(version,screen_output,error) + case default call fatal_error(error,'compiler_get_version not yet implemented for compiler '//self%fc) return end select -end function compiler_get_version +end subroutine compiler_get_version !> Ensure a windows path is converted to a DOS path if it contains spaces function get_dos_path(path,error) From 4ce83a759909bedbdaaa6b46daa451c9b75dc42d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 11:40:14 -0500 Subject: [PATCH 322/799] MS-MPI build via MSYS2 completed --- src/fpm_meta.f90 | 96 +++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 2c69a2add0..19e1c756df 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -470,9 +470,9 @@ logical function msmpi_init(this,compiler,error) result(found) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: incdir,libdir,post,reall,msysdir + character(len=:), allocatable :: incdir,windir,libdir,post,reall,msysdir type(version_t) :: ver,ver10 - type(string_t) :: path + type(string_t) :: cpath,msys_path logical :: msys2 !> Default: not found @@ -488,66 +488,76 @@ logical function msmpi_init(this,compiler,error) result(found) endif is_minGW - !> Find include and library directories of the MS-MPI SDK - incdir = get_env('MSMPI_INC') + ! Check we're on a 64-bit environment if (is_64bit_environment()) then libdir = get_env('MSMPI_LIB64') post = 'x64' else libdir = get_env('MSMPI_LIB32') post = 'x86' + + !> Not working on 32-bit Windows yet + call fatal_error(error,'MS-MPI error: this package requires 64-bit Windows environment') + return + end if - if (verbose) print 1, 'include',incdir,exists(incdir) - if (verbose) print 1, 'library',libdir,exists(libdir) + ! Check that the runtime is installed + windir = get_env('WINDIR') + call get_absolute_path(join_path(windir,'system32\msmpi.dll'),libdir,error) + if (allocated(error)) return - ! Both directories need be defined and existent - if (len_trim(incdir)<=0 .or. len_trim(libdir)<=0) return - if (.not.exists(incdir) .or. .not.exists(libdir)) return + if (len_trim(libdir)<=0 .or. .not.exists(libdir)) then + call fatal_error(error,'MS-MPI error: msmpi.dll is missing. Is MS-MPI installed on this system?') + return + end if ! Success! found = .true. - ! gfortran>=10 is incompatible with the old-style mpif.h MS-MPI headers. - ! If so, add flags to allow old-style BOZ constants in mpif.h - allow_BOZ: if (compiler%id==id_gcc) then + ! Init ms-mpi + call destroy(this) - call new_version(ver10,'10.0.0',error) + ! MSYS2 provides a pre-built static msmpi.dll.a library. Use that if possible + use_prebuilt: if (msys2) then + + ! MSYS executables are in %MSYS_ROOT%/bin + call compiler_get_path(compiler,cpath,error) if (allocated(error)) return - if (ver>=ver10) then - this%has_build_flags = .true. - this%flags = string_t(' -fallow-invalid-boz') - end if + call get_absolute_path(join_path(cpath%s,'..'),msys_path%s,error) + if (allocated(error)) return - endif allow_BOZ + call get_absolute_path(join_path(msys_path%s,'include'),incdir,error) + if (allocated(error)) return - ! Init ms-mpi - call destroy(this) + call get_absolute_path(join_path(msys_path%s,'lib'),libdir,error) + if (allocated(error)) return - ! MSYS2 provides a pre-built static msmpi.dll.a library. Use that if possible - use_prebuilt: if (msys2) then + if (verbose) print 1, 'include',incdir,exists(incdir) + if (verbose) print 1, 'library',libdir,exists(libdir) - call compiler_get_path(compiler,path,error) + ! Check that the necessary files exist + call get_absolute_path(join_path(libdir,'libmsmpi.dll.a'),post,error) if (allocated(error)) return - print *, 'compiler path: '//path%s - stop + if (len_trim(post)<=0 .or. .not.exists(post)) then + call fatal_error(error,'MS-MPI available through the MSYS2 system not found. '// & + 'Run or your system-specific version to install.') + return + end if - ! Add dir path + ! Add dir cpath this%has_link_flags = .true. - !this%link_flags = string_t(' -L'//get_dos_path(libdir,error)) - this%link_flags = string_t(' -LC:\msys64\mingw64\lib') + this%link_flags = string_t(' -L'//get_dos_path(libdir,error)) this%has_link_libraries = .true. this%link_libs = [string_t('msmpi.dll')] - !this%link_libs = [string_t('msmpi'),string_t('msmpifec'),string_t('msmpifmc')] if (allocated(error)) return this%has_include_dirs = .true. - this%incl_dirs = [string_t(get_dos_path(incdir,error)), & - string_t(get_dos_path(incdir//post,error))] + this%incl_dirs = [string_t(get_dos_path(incdir,error))] if (allocated(error)) return else @@ -577,6 +587,20 @@ logical function msmpi_init(this,compiler,error) result(found) this%fortran%implicit_typing = .true. this%fortran%implicit_external = .true. + ! gfortran>=10 is incompatible with the old-style mpif.h MS-MPI headers. + ! If so, add flags to allow old-style BOZ constants in mpif.h + allow_BOZ: if (compiler%id==id_gcc) then + + call new_version(ver10,'10.0.0',error) + if (allocated(error)) return + + if (ver>=ver10) then + this%has_build_flags = .true. + this%flags = string_t(' -fallow-invalid-boz') + end if + + endif allow_BOZ + else !> Not on Windows @@ -732,6 +756,12 @@ function get_dos_path(path,error) character(:), allocatable :: redirect,screen_output,line integer :: stat,cmdstat,iunit,last + ! Non-Windows OS + if (get_os_type()/=OS_WINDOWS) then + get_dos_path = path + return + end if + ! Trim path first get_dos_path = trim(path) @@ -770,11 +800,11 @@ function get_dos_path(path,error) end if command_OK + get_dos_path = trim(adjustl(screen_output)) + endif has_spaces !> Ensure there are no trailing slashes - get_dos_path = trim(adjustl(screen_output)) - last = len_trim(get_dos_path) if (last>1 .and. get_dos_path(last:last)=='/' .or. get_dos_path(last:last)=='\') get_dos_path = get_dos_path(1:last-1) From 4f450f8bf52d2c7db5076916e4fbf58308c742f0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 11:41:35 -0500 Subject: [PATCH 323/799] fix truncated line --- src/fpm_meta.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 19e1c756df..c4be59ac25 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -543,7 +543,8 @@ logical function msmpi_init(this,compiler,error) result(found) if (len_trim(post)<=0 .or. .not.exists(post)) then call fatal_error(error,'MS-MPI available through the MSYS2 system not found. '// & - 'Run or your system-specific version to install.') + 'Run '// & + 'or your system-specific version to install.') return end if From 376e3b839f986e8ebe81e37898fe3ba385b06712 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 11:47:30 -0500 Subject: [PATCH 324/799] Check MS-MPI runtime present; add run command --- src/fpm_meta.f90 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c4be59ac25..8b57cb6f1b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -44,10 +44,12 @@ module fpm_meta logical :: has_build_flags = .false. logical :: has_include_dirs = .false. logical :: has_dependencies = .false. + logical :: has_run_command = .false. !> List of compiler flags and options to be added type(string_t) :: flags type(string_t) :: link_flags + type(string_t) :: run_command type(string_t), allocatable :: incl_dirs(:) type(string_t), allocatable :: link_libs(:) @@ -98,11 +100,13 @@ elemental subroutine destroy(this) this%has_build_flags = .false. this%has_include_dirs = .false. this%has_dependencies = .false. + this%has_run_command = .false. if (allocated(this%fortran)) deallocate(this%fortran) if (allocated(this%version)) deallocate(this%version) if (allocated(this%flags%s)) deallocate(this%flags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) + if (allocated(this%run_command%s)) deallocate(this%run_command%s) if (allocated(this%link_libs)) deallocate(this%link_libs) if (allocated(this%dependency)) deallocate(this%dependency) if (allocated(this%incl_dirs)) deallocate(this%incl_dirs) @@ -470,7 +474,7 @@ logical function msmpi_init(this,compiler,error) result(found) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: incdir,windir,libdir,post,reall,msysdir + character(len=:), allocatable :: incdir,windir,libdir,bindir,post,reall,msysdir type(version_t) :: ver,ver10 type(string_t) :: cpath,msys_path logical :: msys2 @@ -507,6 +511,12 @@ logical function msmpi_init(this,compiler,error) result(found) call get_absolute_path(join_path(windir,'system32\msmpi.dll'),libdir,error) if (allocated(error)) return + bindir = get_env('MSMPI_BIN') + if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then + call fatal_error(error,'MS-MPI error: MS-MPI Runtime directory is missing. check environment variable %MSMPI_BIN%.') + return + end if + if (len_trim(libdir)<=0 .or. .not.exists(libdir)) then call fatal_error(error,'MS-MPI error: msmpi.dll is missing. Is MS-MPI installed on this system?') return @@ -602,6 +612,10 @@ logical function msmpi_init(this,compiler,error) result(found) endif allow_BOZ + !> Add default run command + this%has_run_command = .true. + this%run_command = string_t(get_dos_path(bindir,error)//' np * ') + else !> Not on Windows From b95c9f467877e85eaa1b53ecd9a80515ebafcdb7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 11:55:56 -0500 Subject: [PATCH 325/799] add MPI runner to settings%runner --- src/fpm_meta.f90 | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 8b57cb6f1b..5dffe74f81 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -17,6 +17,7 @@ module fpm_meta use fpm_error, only: error_t, fatal_error, syntax_error, fpm_stop use fpm_compiler use fpm_model +use fpm_command_line use fpm_manifest_dependency, only: dependency_config_t use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t @@ -69,9 +70,10 @@ module fpm_meta procedure :: new => init_from_name !> Add metapackage dependencies to the model + procedure, private :: resolve_cmd procedure, private :: resolve_model procedure, private :: resolve_package_config - generic :: resolve => resolve_model,resolve_package_config + generic :: resolve => resolve_cmd,resolve_model,resolve_package_config end type metapackage_t @@ -217,6 +219,30 @@ subroutine init_stdlib(this,compiler,error) end subroutine init_stdlib +! Resolve metapackage dependencies into the command line settings +subroutine resolve_cmd(self,settings,error) + class(metapackage_t), intent(in) :: self + class(fpm_cmd_settings), intent(inout) :: settings + type(error_t), allocatable, intent(out) :: error + + ! Add customize run commands + if (self%has_run_command) then + + select type (cmd=>settings) + class is (fpm_run_settings) ! includes fpm_test_settings + + if (.not.allocated(cmd%runner)) then + cmd%runner = self%run_command%s + else + cmd%runner = self%run_command%s//' '//cmd%runner + end if + + end select + + endif + +end subroutine resolve_cmd + ! Resolve metapackage dependencies into the model subroutine resolve_model(self,model,error) class(metapackage_t), intent(in) :: self From b0ca7f87d01fe18ac2f3dd57719b4124c754a759 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 20 Apr 2023 12:06:16 -0500 Subject: [PATCH 326/799] add runner wrapper for `mpiexec -np *`: works! --- src/fpm.f90 | 10 ++++---- src/fpm/cmd/install.f90 | 4 ++-- src/fpm_meta.f90 | 52 ++++++++++++----------------------------- 3 files changed, 22 insertions(+), 44 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index e23aa3fcdb..07666fb2f9 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -36,7 +36,7 @@ module fpm !> Constructs a valid fpm model from command line settings and the toml manifest. subroutine build_model(model, settings, package, error) type(fpm_model_t), intent(out) :: model - type(fpm_build_settings), intent(in) :: settings + class(fpm_build_settings), intent(inout) :: settings type(package_config_t), intent(inout) :: package type(error_t), allocatable, intent(out) :: error @@ -72,7 +72,7 @@ subroutine build_model(model, settings, package, error) model%module_prefix = package%build%module_prefix ! Resolve meta-dependencies into the package and the model - call resolve_metapackages(model,package,error) + call resolve_metapackages(model,package,settings,error) if (allocated(error)) return ! Create dependencies @@ -415,7 +415,7 @@ subroutine check_module_names(model, error) end subroutine check_module_names subroutine cmd_build(settings) -type(fpm_build_settings), intent(in) :: settings +type(fpm_build_settings), intent(inout) :: settings type(package_config_t) :: package type(fpm_model_t) :: model @@ -452,7 +452,7 @@ subroutine cmd_build(settings) end subroutine cmd_build subroutine cmd_run(settings,test) - class(fpm_run_settings), intent(in) :: settings + class(fpm_run_settings), intent(inout) :: settings logical, intent(in) :: test integer :: i, j, col_width @@ -475,7 +475,7 @@ subroutine cmd_run(settings,test) call fpm_stop(1, '*cmd_run* Package error: '//error%message) end if - call build_model(model, settings%fpm_build_settings, package, error) + call build_model(model, settings, package, error) if (allocated(error)) then call fpm_stop(1, '*cmd_run* Model error: '//error%message) end if diff --git a/src/fpm/cmd/install.f90 b/src/fpm/cmd/install.f90 index c260bfc4df..69375a88be 100644 --- a/src/fpm/cmd/install.f90 +++ b/src/fpm/cmd/install.f90 @@ -22,7 +22,7 @@ module fpm_cmd_install !> Entry point for the fpm-install subcommand subroutine cmd_install(settings) !> Representation of the command line settings - type(fpm_install_settings), intent(in) :: settings + type(fpm_install_settings), intent(inout) :: settings type(package_config_t) :: package type(error_t), allocatable :: error type(fpm_model_t) :: model @@ -34,7 +34,7 @@ subroutine cmd_install(settings) call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) call handle_error(error) - call build_model(model, settings%fpm_build_settings, package, error) + call build_model(model, settings, package, error) call handle_error(error) call targets_from_sources(targets, model, settings%prune, error) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 5dffe74f81..6e6bf81b77 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -322,8 +322,10 @@ end function dn end subroutine resolve_package_config ! Add named metapackage dependency to the model -subroutine add_metapackage_model(model,name,error) +subroutine add_metapackage_model(model,package,settings,name,error) type(fpm_model_t), intent(inout) :: model + type(package_config_t), intent(inout) :: package + class(fpm_cmd_settings), intent(inout) :: settings character(*), intent(in) :: name type(error_t), allocatable, intent(out) :: error @@ -333,43 +335,25 @@ subroutine add_metapackage_model(model,name,error) call meta%new(name,model%compiler,error) if (allocated(error)) return - !> Add it to the model + !> Add it into the model call meta%resolve(model,error) if (allocated(error)) return -end subroutine add_metapackage_model - -! Add named metapackage dependency to the model -subroutine add_metapackage_config(package,compiler,name,error) - type(package_config_t), intent(inout) :: package - type(compiler_t), intent(in) :: compiler - character(*), intent(in) :: name - type(error_t), allocatable, intent(out) :: error - - type(metapackage_t) :: meta - - !> Init metapackage - call meta%new(name,compiler,error) - if (allocated(error)) return - - !> Add it to the model + !> Add it into the package call meta%resolve(package,error) if (allocated(error)) return - ! Temporary - if (name=="mpi") then - - - - - end if + !> Add it into the settings + call meta%resolve(settings,error) + if (allocated(error)) return -end subroutine add_metapackage_config +end subroutine add_metapackage_model !> Resolve all metapackages into the package config -subroutine resolve_metapackage_model(model,package,error) +subroutine resolve_metapackage_model(model,package,settings,error) type(fpm_model_t), intent(inout) :: model type(package_config_t), intent(inout) :: package + class(fpm_build_settings), intent(inout) :: settings type(error_t), allocatable, intent(out) :: error ! Dependencies are added to the package config, so they're properly resolved @@ -382,17 +366,13 @@ subroutine resolve_metapackage_model(model,package,error) ! OpenMP if (package%meta%openmp) then - call add_metapackage_model(model,"openmp",error) - if (allocated(error)) return - call add_metapackage_config(package,model%compiler,"openmp",error) + call add_metapackage_model(model,package,settings,"openmp",error) if (allocated(error)) return endif ! stdlib if (package%meta%stdlib) then - call add_metapackage_model(model,"stdlib",error) - if (allocated(error)) return - call add_metapackage_config(package,model%compiler,"stdlib",error) + call add_metapackage_model(model,package,settings,"stdlib",error) if (allocated(error)) return endif @@ -403,9 +383,7 @@ subroutine resolve_metapackage_model(model,package,error) ! MPI if (package%meta%mpi) then - call add_metapackage_model(model,"mpi",error) - if (allocated(error)) return - call add_metapackage_config(package,model%compiler,"mpi",error) + call add_metapackage_model(model,package,settings,"mpi",error) if (allocated(error)) return endif @@ -640,7 +618,7 @@ logical function msmpi_init(this,compiler,error) result(found) !> Add default run command this%has_run_command = .true. - this%run_command = string_t(get_dos_path(bindir,error)//' np * ') + this%run_command = string_t(join_path(get_dos_path(bindir,error),'mpiexec')//' -np * ') else From a40bda0682e5362e6c615f6fb7efb108fe4640f1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 08:52:11 +0200 Subject: [PATCH 327/799] fix openmpi compiler wrapper --- src/fpm_meta.f90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 6e6bf81b77..b3c4af16d7 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -400,8 +400,8 @@ subroutine init_mpi(this,compiler,error) type(string_t) :: output character(256) :: msg_out character(len=:), allocatable :: tokens(:) - integer :: mpif90,ic,icpp,i - logical :: wcfit,found + integer :: wcfit,ic,icpp,i + logical :: found !> Cleanup @@ -413,7 +413,7 @@ subroutine init_mpi(this,compiler,error) wcfit = wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) - if (allocated(error) .or. .not.wcfit) then + if (allocated(error) .or. wcfit==0) then !> No wrapper compiler fit. Are we on Windows? use MSMPI-specific search found = msmpi_init(this,compiler,error) @@ -428,7 +428,7 @@ subroutine init_mpi(this,compiler,error) else !> Initialize MPI package from wrapper command - call init_mpi_from_wrapper(this,compiler,fort_wrappers(mpif90),error) + call init_mpi_from_wrapper(this,compiler,fort_wrappers(wcfit),error) if (allocated(error)) return end if @@ -446,7 +446,7 @@ logical function is_64bit_environment() end function is_64bit_environment !> Check if there is a wrapper-compiler fit -logical function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) +integer function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) type(string_t), allocatable, intent(in) :: fort_wrappers(:),c_wrappers(:),cpp_wrappers(:) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error @@ -454,7 +454,7 @@ logical function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,comp logical :: has_wrappers integer :: mpif90 - wrapper_compiler_fit = .false. + wrapper_compiler_fit = 0 !> Were any wrappers found? has_wrappers = size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)>0 @@ -466,7 +466,7 @@ logical function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,comp if (allocated(error)) return !> Was a valid wrapper found? - wrapper_compiler_fit = mpif90>0 + wrapper_compiler_fit = mpif90 endif From e5ceb064a3c0aeb3a2024dab73f338b2387aff9f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 08:55:03 +0200 Subject: [PATCH 328/799] update regex dependency --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 6e607b3b43..c4a7e4fbbd 100644 --- a/fpm.toml +++ b/fpm.toml @@ -15,7 +15,7 @@ toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" fortran-regex.git = "https://github.com/perazz/fortran-regex" -fortran-regex.tag = "1.1.0" +fortran-regex.tag = "1.1.1" jonquil.git = "https://github.com/toml-f/jonquil" jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" From 7dc2b945b43afb41db7c63d086cbcb5eb8103b58 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 09:30:02 +0200 Subject: [PATCH 329/799] implement OpenMPI runner command --- src/fpm_meta.f90 | 108 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 23 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index b3c4af16d7..e30a93688b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -631,24 +631,30 @@ logical function msmpi_init(this,compiler,error) result(found) end function msmpi_init -!> Return compiler path -subroutine compiler_get_path(self,path,error) - type(compiler_t), intent(in) :: self - type(string_t), intent(out) :: path +!> Find the location of a valid command +subroutine find_command_location(command,path,echo,verbose,error) + character(*), intent(in) :: command + character(len=:), allocatable, intent(out) :: path + logical, optional, intent(in) :: echo,verbose type(error_t), allocatable, intent(out) :: error character(:), allocatable :: tmp_file,screen_output,line,fullpath integer :: stat,iunit,ire,length + if (len_trim(command)<=0) then + call fatal_error(error,'empty command provided in find_command_location') + return + end if + tmp_file = get_temp_filename() if (get_os_type()==OS_WINDOWS) then - call run("where "//self%fc, echo=self%echo, verbose=self%verbose, redirect=tmp_file, exitstat=stat) + call run("where "//command, echo=echo, verbose=verbose, redirect=tmp_file, exitstat=stat) else - call run("which "//self%fc, echo=self%echo, verbose=self%verbose, redirect=tmp_file, exitstat=stat) + call run("which "//command, echo=echo, verbose=verbose, redirect=tmp_file, exitstat=stat) end if if (stat/=0) then - call fatal_error(error,'compiler_get_path failed for '//self%fc) + call fatal_error(error,'compiler_get_path failed for '//command) return end if @@ -668,7 +674,7 @@ subroutine compiler_get_path(self,path,error) ! Close and delete file close(iunit,status='delete') else - call fatal_error(error,'cannot read temporary file from successful compiler_get_path') + call fatal_error(error,'cannot read temporary file from successful find_command_location') return endif @@ -680,27 +686,66 @@ subroutine compiler_get_path(self,path,error) fullpath = screen_output endif multiline if (len_trim(fullpath)<1) then - call fatal_error(error,'no paths found to the current compiler ('//self%fc//')') + call fatal_error(error,'no paths found to command ('//command//')') return end if ! Extract path only - length = index(fullpath,self%fc,BACK=.true.) + length = index(fullpath,command,BACK=.true.) if (length<=0) then - call fatal_error(error,'full path to the current compiler ('//self%fc//') does not include compiler name') + call fatal_error(error,'full path to command ('//command//') does not include command name') return elseif (length==1) then ! Compiler is in the current folder - call get_absolute_path('.',path%s,error) + call get_absolute_path('.',path,error) else - path%s = canon_path(fullpath(1:length-1)) + path = canon_path(fullpath(1:length-1)) end if - if (.not.is_dir(path%s)) then - call fatal_error(error,'full path to the current compiler ('//self%fc//') is not a directory') + if (.not.is_dir(path)) then + call fatal_error(error,'full path to command ('//command//') is not a directory') return end if +end subroutine find_command_location + +!> Get MPI runner in $PATH +subroutine get_mpi_runner(command,verbose,error) + type(string_t), intent(out) :: command + logical, optional, intent(in) :: verbose + type(error_t), allocatable, intent(out) :: error + + character(*), parameter :: try(*) = ['mpiexec','mpirun '] + integer :: itri + logical :: success + + ! Try several commands + do itri=1,size(try) + call find_command_location(trim(try(itri)),command%s,verbose=verbose,error=error) + + ! Success! + success = len_trim(command%s)>0 .and. .not.allocated(error) + if (success) then + command%s = join_path(command%s,trim(try(itri))) + return + endif + + end do + + ! No valid command found + call fatal_error(error,'cannot find a valid mpi runner command') + return + +end subroutine get_mpi_runner + +!> Return compiler path +subroutine compiler_get_path(self,path,error) + type(compiler_t), intent(in) :: self + type(string_t), intent(out) :: path + type(error_t), allocatable, intent(out) :: error + + call find_command_location(self%fc,path%s,self%echo,self%verbose,error) + end subroutine compiler_get_path !> Return compiler version @@ -865,6 +910,12 @@ subroutine init_mpi_from_wrapper(this,compiler,fort_wrapper,error) allocate(this%version,source=version) end if + !> Add default run command, if present + this%run_command = mpi_wrapper_query(fort_wrapper,'runner',verbose,error) + if (allocated(error)) return + this%has_run_command = len_trim(this%run_command)>0 + + end subroutine init_mpi_from_wrapper !> Match one of the available compiler wrappers with the current compiler @@ -1012,7 +1063,7 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp if(present(verbose))then echo_local=verbose else - echo_local=.true. + echo_local=.false. end if ! No redirection and non-verbose output @@ -1062,7 +1113,7 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp screen_output%s = screen_output%s//new_line('a')//line - if (verbose) write(*,'(A)') trim(line) + if (echo_local) write(*,'(A)') trim(line) end do ! Close and delete file @@ -1137,7 +1188,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:command')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:command')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then @@ -1160,7 +1211,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:compile')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:compile')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then @@ -1184,7 +1235,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:link')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:link')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then @@ -1208,7 +1259,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:libdirs')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:libdirs')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then @@ -1230,7 +1281,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:incdirs')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:incdirs')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then @@ -1252,7 +1303,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:version')],verbose=.true., & + call run_mpi_wrapper(wrapper,[string_t('--showme:version')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) if (stat/=0 .or. .not.success) then @@ -1283,6 +1334,17 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( end select + ! Get path to the MPI runner command + case ('runner') + + select case (mpi) + case (MPI_TYPE_OPENMPI) + call get_mpi_runner(screen,verbose,error) + case default + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + return + end select + case default; call fatal_error(error,'an invalid MPI wrapper command ('//command//& ') was invoked for wrapper <'//wrapper%s//'>.') From e8576a0537fe38274727630def6c92eb6e3d9c31 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 10:08:57 +0200 Subject: [PATCH 330/799] make flags language-specific (Fortran, C, C++) --- src/fpm_meta.f90 | 105 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e30a93688b..7ac87b91d4 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -43,12 +43,18 @@ module fpm_meta logical :: has_link_libraries = .false. logical :: has_link_flags = .false. logical :: has_build_flags = .false. + logical :: has_fortran_flags = .false. + logical :: has_c_flags = .false. + logical :: has_cxx_flags = .false. logical :: has_include_dirs = .false. logical :: has_dependencies = .false. logical :: has_run_command = .false. !> List of compiler flags and options to be added type(string_t) :: flags + type(string_t) :: fflags + type(string_t) :: cflags + type(string_t) :: cxxflags type(string_t) :: link_flags type(string_t) :: run_command type(string_t), allocatable :: incl_dirs(:) @@ -87,19 +93,27 @@ module fpm_meta integer, parameter :: MPI_TYPE_INTEL = 3 integer, parameter :: MPI_TYPE_MSMPI = 4 + + !> Debugging information logical, parameter, private :: verbose = .true. +integer, parameter, private :: WRAPPER_FORTRAN = 1 +integer, parameter, private :: WRAPPER_C = 2 +integer, parameter, private :: WRAPPER_CXX = 3 + contains !> Clean the metapackage structure elemental subroutine destroy(this) class(metapackage_t), intent(inout) :: this - this%has_link_libraries = .false. this%has_link_flags = .false. this%has_build_flags = .false. + this%has_fortran_flags = .false. + this%has_c_flags = .false. + this%has_cxx_flags = .false. this%has_include_dirs = .false. this%has_dependencies = .false. this%has_run_command = .false. @@ -107,6 +121,9 @@ elemental subroutine destroy(this) if (allocated(this%fortran)) deallocate(this%fortran) if (allocated(this%version)) deallocate(this%version) if (allocated(this%flags%s)) deallocate(this%flags%s) + if (allocated(this%fflags%s)) deallocate(this%fflags%s) + if (allocated(this%cflags%s)) deallocate(this%cflags%s) + if (allocated(this%cxxflags%s)) deallocate(this%cxxflags%s) if (allocated(this%link_flags%s)) deallocate(this%link_flags%s) if (allocated(this%run_command%s)) deallocate(this%run_command%s) if (allocated(this%link_libs)) deallocate(this%link_libs) @@ -249,13 +266,20 @@ subroutine resolve_model(self,model,error) type(fpm_model_t), intent(inout) :: model type(error_t), allocatable, intent(out) :: error - ! For now, additional flags are assumed to apply to all sources + ! Add global build flags, to apply to all sources if (self%has_build_flags) then model%fortran_compile_flags = model%fortran_compile_flags//self%flags%s model%c_compile_flags = model%c_compile_flags//self%flags%s model%cxx_compile_flags = model%cxx_compile_flags//self%flags%s endif + ! Add language-specific flags + print *, 'has fortran,c,cpp',self%has_fortran_flags,self%has_c_flags,self%has_cxx_flags + stop + if (self%has_fortran_flags) model%fortran_compile_flags = model%fortran_compile_flags//self%fflags%s + if (self%has_c_flags) model%c_compile_flags = model%c_compile_flags//self%cflags%s + if (self%has_cxx_flags) model%cxx_compile_flags = model%cxx_compile_flags//self%cxxflags%s + if (self%has_link_flags) then model%link_flags = model%link_flags//self%link_flags%s end if @@ -268,8 +292,6 @@ subroutine resolve_model(self,model,error) model%include_dirs = [model%include_dirs,self%incl_dirs] end if - - end subroutine resolve_model subroutine resolve_package_config(self,package,error) @@ -397,10 +419,10 @@ subroutine init_mpi(this,compiler,error) type(string_t), allocatable :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) - type(string_t) :: output + type(string_t) :: output,fwrap,cwrap,cxxwrap character(256) :: msg_out character(len=:), allocatable :: tokens(:) - integer :: wcfit,ic,icpp,i + integer :: wcfit(3),ic,icpp,i logical :: found @@ -413,7 +435,7 @@ subroutine init_mpi(this,compiler,error) wcfit = wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) - if (allocated(error) .or. wcfit==0) then + if (allocated(error) .or. all(wcfit==0)) then !> No wrapper compiler fit. Are we on Windows? use MSMPI-specific search found = msmpi_init(this,compiler,error) @@ -427,8 +449,12 @@ subroutine init_mpi(this,compiler,error) else + if (wcfit(WRAPPER_FORTRAN)>0) fwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) + if (wcfit(WRAPPER_C)>0) cwrap = c_wrappers (wcfit(WRAPPER_C)) + if (wcfit(WRAPPER_CXX)>0) cxxwrap = cpp_wrappers (wcfit(WRAPPER_CXX)) + !> Initialize MPI package from wrapper command - call init_mpi_from_wrapper(this,compiler,fort_wrappers(wcfit),error) + call init_mpi_from_wrappers(this,compiler,fwrap,cwrap,cxxwrap,error) if (allocated(error)) return end if @@ -446,27 +472,32 @@ logical function is_64bit_environment() end function is_64bit_environment !> Check if there is a wrapper-compiler fit -integer function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) +function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) result(wrap) type(string_t), allocatable, intent(in) :: fort_wrappers(:),c_wrappers(:),cpp_wrappers(:) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error + integer :: wrap(3) logical :: has_wrappers - integer :: mpif90 + integer :: mpif90,mpic,mpicxx + type(error_t), allocatable :: wrap_error - wrapper_compiler_fit = 0 + wrap = 0 !> Were any wrappers found? has_wrappers = size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)>0 if (has_wrappers) then - !> Find an MPI wrapper that matches the current compiler - mpif90 = mpi_compiler_match(fort_wrappers,compiler,error) - if (allocated(error)) return + !> Find a Fortran wrapper for the current compiler + wrap(WRAPPER_FORTRAN) = mpi_compiler_match(fort_wrappers,compiler,wrap_error) + wrap(WRAPPER_C ) = mpi_compiler_match(c_wrappers,compiler,wrap_error) + wrap(WRAPPER_CXX ) = mpi_compiler_match(cpp_wrappers,compiler,wrap_error) - !> Was a valid wrapper found? - wrapper_compiler_fit = mpif90 + if (all(wrap==0)) then + call fatal_error(error,'no valid wrappers match current compiler, '//compiler_name(compiler)) + return + end if endif @@ -875,10 +906,10 @@ function get_dos_path(path,error) end function get_dos_path !> Initialize an MPI metapackage from a valid wrapper command ('mpif90', etc...) -subroutine init_mpi_from_wrapper(this,compiler,fort_wrapper,error) +subroutine init_mpi_from_wrappers(this,compiler,fort_wrapper,c_wrapper,cxx_wrapper,error) class(metapackage_t), intent(inout) :: this type(compiler_t), intent(in) :: compiler - type(string_t), intent(in) :: fort_wrapper + type(string_t), intent(in) :: fort_wrapper,c_wrapper,cxx_wrapper type(error_t), allocatable, intent(out) :: error type(version_t) :: version @@ -894,13 +925,13 @@ subroutine init_mpi_from_wrapper(this,compiler,fort_wrapper,error) ! Add heading space this%link_flags = string_t(' '//this%link_flags%s) - ! Get build flags - this%flags = mpi_wrapper_query(fort_wrapper,'flags',verbose,error) + ! Add language-specific flags + call set_language_flags(fort_wrapper,this%has_fortran_flags,this%fflags,verbose,error) + if (allocated(error)) return + call set_language_flags(c_wrapper,this%has_c_flags,this%cflags,verbose,error) + if (allocated(error)) return + call set_language_flags(cxx_wrapper,this%has_cxx_flags,this%cxxflags,verbose,error) if (allocated(error)) return - this%has_build_flags = len_trim(this%flags)>0 - - ! Add heading space - this%flags = string_t(' '//this%flags%s) ! Get library version version = mpi_version_get(fort_wrapper,error) @@ -915,8 +946,32 @@ subroutine init_mpi_from_wrapper(this,compiler,fort_wrapper,error) if (allocated(error)) return this%has_run_command = len_trim(this%run_command)>0 + contains + + subroutine set_language_flags(wrapper,has_flags,flags,verbose,error) + type(string_t), intent(in) :: wrapper + logical, intent(inout) :: has_flags + type(string_t), intent(inout) :: flags + logical, intent(in) :: verbose + type(error_t), allocatable, intent(out) :: error + + ! Get build flags for each language + if (len_trim(wrapper)>0) then + flags = mpi_wrapper_query(wrapper,'flags',verbose,error) + + print *, 'flags=',flags%s,' error=',allocated(error),' wrapper=',wrapper%s + + if (allocated(error)) return + this%has_fortran_flags = len_trim(flags)>0 + + ! Add heading space + flags = string_t(' '//flags%s) + endif + + end subroutine set_language_flags + -end subroutine init_mpi_from_wrapper +end subroutine init_mpi_from_wrappers !> Match one of the available compiler wrappers with the current compiler integer function mpi_compiler_match(wrappers,compiler,error) From 82d03c5621acc10ebe8b97e839f6f5905aabd70d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 10:14:11 +0200 Subject: [PATCH 331/799] fix: language flags --- src/fpm_meta.f90 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 7ac87b91d4..fc8ef744c9 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -274,8 +274,6 @@ subroutine resolve_model(self,model,error) endif ! Add language-specific flags - print *, 'has fortran,c,cpp',self%has_fortran_flags,self%has_c_flags,self%has_cxx_flags - stop if (self%has_fortran_flags) model%fortran_compile_flags = model%fortran_compile_flags//self%fflags%s if (self%has_c_flags) model%c_compile_flags = model%c_compile_flags//self%cflags%s if (self%has_cxx_flags) model%cxx_compile_flags = model%cxx_compile_flags//self%cxxflags%s @@ -449,6 +447,8 @@ subroutine init_mpi(this,compiler,error) else + print *, 'wcfit=',wcfit + if (wcfit(WRAPPER_FORTRAN)>0) fwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) if (wcfit(WRAPPER_C)>0) cwrap = c_wrappers (wcfit(WRAPPER_C)) if (wcfit(WRAPPER_CXX)>0) cxxwrap = cpp_wrappers (wcfit(WRAPPER_CXX)) @@ -962,7 +962,7 @@ subroutine set_language_flags(wrapper,has_flags,flags,verbose,error) print *, 'flags=',flags%s,' error=',allocated(error),' wrapper=',wrapper%s if (allocated(error)) return - this%has_fortran_flags = len_trim(flags)>0 + has_flags = len_trim(flags)>0 ! Add heading space flags = string_t(' '//flags%s) @@ -984,6 +984,13 @@ integer function mpi_compiler_match(wrappers,compiler,error) character(128) :: msg_out type(compiler_t) :: mpi_compiler + !> If there's only one available wrapper, we're forced to use that one regardless of + !> what compiler it was bound to + if (size(wrappers)==1) then + mpi_compiler_match = 1 + return + end if + mpi_compiler_match = 0 do i=1,size(wrappers) From 7076f9f362475360f0b7487177b6ebe1fdae1dc0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 10:15:44 +0200 Subject: [PATCH 332/799] improve verbose outout --- src/fpm_meta.f90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index fc8ef744c9..7a623265ef 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -447,8 +447,6 @@ subroutine init_mpi(this,compiler,error) else - print *, 'wcfit=',wcfit - if (wcfit(WRAPPER_FORTRAN)>0) fwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) if (wcfit(WRAPPER_C)>0) cwrap = c_wrappers (wcfit(WRAPPER_C)) if (wcfit(WRAPPER_CXX)>0) cxxwrap = cpp_wrappers (wcfit(WRAPPER_CXX)) @@ -959,13 +957,14 @@ subroutine set_language_flags(wrapper,has_flags,flags,verbose,error) if (len_trim(wrapper)>0) then flags = mpi_wrapper_query(wrapper,'flags',verbose,error) - print *, 'flags=',flags%s,' error=',allocated(error),' wrapper=',wrapper%s - if (allocated(error)) return has_flags = len_trim(flags)>0 ! Add heading space flags = string_t(' '//flags%s) + + if (verbose) print *, 'MPI set language flags from wrapper <',wrapper%s,'>: flags=',flags%s + endif end subroutine set_language_flags From 92507c354b2ffdc85ce11b062ff7acc494a02ab7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 10:50:16 +0200 Subject: [PATCH 333/799] identify MPICH wrappers --- src/fpm_meta.f90 | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 7a623265ef..7e5ecadb80 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -451,6 +451,14 @@ subroutine init_mpi(this,compiler,error) if (wcfit(WRAPPER_C)>0) cwrap = c_wrappers (wcfit(WRAPPER_C)) if (wcfit(WRAPPER_CXX)>0) cxxwrap = cpp_wrappers (wcfit(WRAPPER_CXX)) + !> If there's only an available Fortran wrapper, and the compiler's different than fpm's baseline + !> fortran compiler suite, we still want to enable C language flags as that is most likely being + !> ABI-compatible anyways. However, issues may arise. + !> see e.g. Homebrew with clabng C/C++ and GNU fortran at https://gitlab.kitware.com/cmake/cmake/-/issues/18139 + if (wcfit(WRAPPER_FORTRAN)>0 .and. wcfit(WRAPPER_C)==0 .and. wcfit(WRAPPER_CXX)==0) then + cwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) + end if + !> Initialize MPI package from wrapper command call init_mpi_from_wrappers(this,compiler,fwrap,cwrap,cxxwrap,error) if (allocated(error)) return @@ -969,7 +977,6 @@ subroutine set_language_flags(wrapper,has_flags,flags,verbose,error) end subroutine set_language_flags - end subroutine init_mpi_from_wrappers !> Match one of the available compiler wrappers with the current compiler @@ -983,13 +990,6 @@ integer function mpi_compiler_match(wrappers,compiler,error) character(128) :: msg_out type(compiler_t) :: mpi_compiler - !> If there's only one available wrapper, we're forced to use that one regardless of - !> what compiler it was bound to - if (size(wrappers)==1) then - mpi_compiler_match = 1 - return - end if - mpi_compiler_match = 0 do i=1,size(wrappers) @@ -1201,22 +1201,26 @@ integer function which_mpi_library(wrapper,verbose) if (is_mpi_wrapper) then + ! Init as currently unsupported library + which_mpi_library = MPI_TYPE_NONE + ! Attempt to decipher which library this wrapper comes from. ! OpenMPI responds to '--showme' calls call run_mpi_wrapper(wrapper,[string_t('--showme')],verbose,& exitcode=stat,cmd_success=is_mpi_wrapper) - if (stat==0 .and. is_mpi_wrapper) then - which_mpi_library = MPI_TYPE_OPENMPI + return + endif - else - - ! This MPI wrapper is of a currently unsupported library - which_mpi_library = MPI_TYPE_NONE - - end if + ! MPICH responds to '-show' calls + call run_mpi_wrapper(wrapper,[string_t('-show')],verbose,& + exitcode=stat,cmd_success=is_mpi_wrapper) + if (stat==0 .and. is_mpi_wrapper) then + which_mpi_library = MPI_TYPE_MPICH + return + endif else From 01e3a7f80ca48987852a136a32f32c7d97581bf3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 11:32:17 +0200 Subject: [PATCH 334/799] implement MPICH commands --- src/fpm_meta.f90 | 102 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 7e5ecadb80..b4cabf2d6d 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1149,7 +1149,6 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp end do endif add_arguments - if (echo_local) print *, '+ ', command ! Test command @@ -1238,7 +1237,9 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( type(error_t), allocatable, intent(out) :: error logical :: success - character(:), allocatable :: redirect_str + character(:), allocatable :: redirect_str,tokens(:) + type(string_t) :: cmdstr + type(compiler_t) :: mpi_compiler integer :: stat,cmdstat,mpi,ire,length ! Get mpi type @@ -1261,6 +1262,21 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( return end if + case (MPI_TYPE_MPICH) + + ! -compile_info returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('-compile-info')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local MPICH library does not support -compile-info') + return + end if + + ! Take out the first command from the whole line + call split(screen%s,tokens,delimiters=' ') + screen%s = tokens(1) + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') @@ -1286,6 +1302,26 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( call remove_new_lines(screen) + case (MPI_TYPE_MPICH) + + call run_mpi_wrapper(wrapper,[string_t('-compile-info')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local MPICH library does not support -compile-info') + return + end if + + ! MPICH reports the full command including the compiler name. Remove it if so + call remove_new_lines(screen) + call split(screen%s,tokens) + call new_compiler(mpi_compiler,tokens(1),tokens(1),tokens(1),echo=.false.,verbose=verbose) + + if (mpi_compiler%id/=id_unknown) then + ! Remove trailing compiler name + screen%s = screen%s(len_trim(tokens(1))+1:) + end if + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') @@ -1299,7 +1335,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( select case (mpi) case (MPI_TYPE_OPENMPI) - ! --showme:command returns the build command of this wrapper + ! --showme:link returns the linker command of this wrapper call run_mpi_wrapper(wrapper,[string_t('--showme:link')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) @@ -1310,6 +1346,26 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( call remove_new_lines(screen) + case (MPI_TYPE_MPICH) + + call run_mpi_wrapper(wrapper,[string_t('-link-info')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local MPICH library does not support -link-info') + return + end if + + ! MPICH reports the full command including the compiler name. Remove it if so + call remove_new_lines(screen) + call split(screen%s,tokens) + call new_compiler(mpi_compiler,tokens(1),tokens(1),tokens(1),echo=.false.,verbose=verbose) + + if (mpi_compiler%id/=id_unknown) then + ! Remove trailing compiler name + screen%s = screen%s(len_trim(tokens(1))+1:) + end if + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') @@ -1392,6 +1448,44 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( end if + case (MPI_TYPE_MPICH) + + !> MPICH offers command "mpichversion" in the same system folder as the MPI wrappers. + !> So, attempt to run that first + cmdstr = string_t('mpichversion') + call run_mpi_wrapper(cmdstr,verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + ! Second option: run mpich wrapper + "-v" + if (stat/=0 .or. .not.success) then + call run_mpi_wrapper(wrapper,[string_t('-v')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + call remove_new_lines(screen) + endif + + ! Third option: mpiexec --version + if (stat/=0 .or. .not.success) then + cmdstr = string_t('mpiexec --version') + call run_mpi_wrapper(cmdstr,verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + endif + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'cannot retrieve MPICH library version from ') + return + else + + ! Extract version + ire = regex(screen%s,'\d+.\d+.\d+',length=length) + if (ire>0 .and. length>0) then + ! Parse version into the object (this should always work) + screen%s = screen%s(ire:ire+length-1) + else + call syntax_error(error,'cannot retrieve MPICH library version.') + end if + + end if + case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') @@ -1403,7 +1497,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case ('runner') select case (mpi) - case (MPI_TYPE_OPENMPI) + case (MPI_TYPE_OPENMPI,MPI_TYPE_MPICH,MPI_TYPE_MSMPI) call get_mpi_runner(screen,verbose,error) case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') From c625cf1aca0598b01cb65925783af4bf32224e1b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 11:37:16 +0200 Subject: [PATCH 335/799] pass flags to c++ on mixed compilers --- src/fpm_meta.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index b4cabf2d6d..b64bc28529 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -457,6 +457,7 @@ subroutine init_mpi(this,compiler,error) !> see e.g. Homebrew with clabng C/C++ and GNU fortran at https://gitlab.kitware.com/cmake/cmake/-/issues/18139 if (wcfit(WRAPPER_FORTRAN)>0 .and. wcfit(WRAPPER_C)==0 .and. wcfit(WRAPPER_CXX)==0) then cwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) + cxxwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) end if !> Initialize MPI package from wrapper command From 079a1979f8157f94b268584b625b02ff0287812a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 11:50:13 +0200 Subject: [PATCH 336/799] create metapackage workflow --- .github/workflows/meta.yml | 172 +++++++++++++++++++ ci/meta_tests.sh | 36 ++++ example_packages/metapackage_stdlib/fpm.toml | 4 - 3 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/meta.yml create mode 100755 ci/meta_tests.sh diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml new file mode 100644 index 0000000000..01bba6f414 --- /dev/null +++ b/.github/workflows/meta.yml @@ -0,0 +1,172 @@ +name: metapackage-tests + +on: + push: + pull_request: + release: + types: [published] + +env: + CI: "ON" # We can detect this in the build system and other vendors implement it + HOMEBREW_NO_ANALYTICS: "ON" # Make Homebrew installation a little quicker + HOMEBREW_NO_AUTO_UPDATE: "ON" + HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" + HOMEBREW_NO_GITHUB_API: "ON" + HOMEBREW_NO_INSTALL_CLEANUP: "ON" + +jobs: + + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-11] ##[ubuntu-latest, macos-11, windows-latest] + gcc_v: [10] # Version of GFortran we want to use + include: + - os: ubuntu-latest + os-arch: linux-x86_64 + release-flags: --flag '--static -g -fbacktrace -O3' + + - os: macos-11 + os-arch: macos-x86_64 + release-flags: --flag '-g -fbacktrace -O3' + + - os: windows-latest + os-arch: windows-x86_64 + release-flags: --flag '--static -g -fbacktrace -O3' + exe: .exe + + env: + FC: gfortran + GCC_V: ${{ matrix.gcc_v }} + + steps: + - name: Checkout code + uses: actions/checkout@v1 + + - name: Install GFortran macOS + if: contains(matrix.os, 'macos') + run: | + ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran + which gfortran-${GCC_V} + which gfortran + # Backport gfortran shared libraries to version 9 folder. This is necessary because all macOS releases of fpm + # have these paths hardcoded in the executable (no PIC?). As the gcc ABIs have not changed from 9 to 10, we + # can just create symbolic links for now. This can be removed when an updated fpm release is built with gcc-10 + mkdir /usr/local/opt/gcc@9 + mkdir /usr/local/opt/gcc@9/lib + mkdir /usr/local/opt/gcc@9/lib/gcc + mkdir /usr/local/opt/gcc@9/lib/gcc/9 + mkdir /usr/local/lib/gcc/9 + ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib + ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib + ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib + + - name: Install GFortran Linux + if: contains(matrix.os, 'ubuntu') + run: | + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \ + --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} + + - name: Install GFortran Windows + if: contains(matrix.os, 'windows') + run: | + Invoke-WebRequest -Uri $Env:GCC_DOWNLOAD -OutFile mingw-w64.zip + Expand-Archive mingw-w64.zip + echo "$pwd\mingw-w64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + env: + GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/10.4.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-10.4.0-mingw-w64msvcrt-10.0.0-r1.zip" + + # Phase 1: Bootstrap fpm with existing version + - name: Install fpm + uses: fortran-lang/setup-fpm@v3 + with: + fpm-version: 'v0.8.0' + + - name: Remove fpm from path + shell: bash + run: | + mv $(which fpm) fpm-bootstrap${{ matrix.exe }} + echo "BOOTSTRAP=$PWD/fpm-bootstrap" >> $GITHUB_ENV + + - name: Build Fortran fpm (bootstrap) + shell: bash + run: | + ${{ env.BOOTSTRAP }} build + + - name: Run Fortran fpm (bootstrap) + shell: bash + run: | + ${{ env.BOOTSTRAP }} run + ${{ env.BOOTSTRAP }} run -- --version + ${{ env.BOOTSTRAP }} run -- --help + + - name: Test Fortran fpm (bootstrap) + shell: bash + run: | + ${{ env.BOOTSTRAP }} test + + - name: Install Fortran fpm (bootstrap) + shell: bash + run: | + ${{ env.BOOTSTRAP }} install + + # Phase 2: Bootstrap fpm with itself + - name: Replace bootstrapping version + shell: bash + run: | + ${{ env.BOOTSTRAP }} run --runner cp -- fpm-debug${{ matrix.exe }} + rm -v ${{ env.BOOTSTRAP }} + echo "FPM=$PWD/fpm-debug" >> $GITHUB_ENV + + - name: Get version (normal) + if: github.event_name != 'release' + shell: bash + run: | + VERSION=$(git rev-parse --short HEAD) + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Get version (release) + if: github.event_name == 'release' + shell: bash + run: | + VERSION=$(echo ${{ github.ref }} | cut -dv -f2) + echo "VERSION=$VERSION" >> $GITHUB_ENV + FPM_VERSION=$(${{ env.FPM }} --version | grep -o '${{ env.REGEX }}') + [ "$VERSION" = "$FPM_VERSION" ] + env: + REGEX: '[0-9]\{1,4\}\.[0-9]\{1,4\}\.[0-9]\{1,4\}' + + - name: Build Fortran fpm + shell: bash + run: | + ${{ env.FPM }} build ${{ matrix.release-flags }} + + - name: Run Fortran fpm + shell: bash + run: | + ${{ env.FPM }} run ${{ matrix.release-flags }} + ${{ env.FPM }} run ${{ matrix.release-flags }} -- --version + ${{ env.FPM }} run ${{ matrix.release-flags }} -- --help + + - name: Install Fortran fpm + shell: bash + run: | + ${{ env.FPM }} install ${{ matrix.release-flags }} + + - name: Package release version + shell: bash + run: | + ${{ env.FPM }} run ${{ matrix.release-flags }} --runner cp -- ${{ env.EXE }} + rm -v ${{ env.FPM }} + echo "FPM_RELEASE=${{ env.EXE }}" >> $GITHUB_ENV + env: + EXE: fpm-${{ env.VERSION }}-${{ matrix.os-arch }}${{ matrix.exe }} + + - name: Run metapackage tests using the release version + shell: bash + run: | + ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" + diff --git a/ci/meta_tests.sh b/ci/meta_tests.sh new file mode 100755 index 0000000000..16d333b530 --- /dev/null +++ b/ci/meta_tests.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -ex + +# *********************** +# This script tests all example packages using any metapackage/system dependencies +# *********************** + +cd "$(dirname $0)/.." + +if [ "$1" ]; then + fpm="$1" +else + fpm=fpm +fi + +# Build example packages +pushd example_packages/ +rm -rf ./*/build + +pushd metapackage_openmp +"$fpm" build +"$fpm" run +popd + +pushd metapackage_stdlib +"$fpm" build +"$fpm" run +popd + +pushd metapackage_mpi +"$fpm" build +"$fpm" run +popd + +# Cleanup +rm -rf ./*/build diff --git a/example_packages/metapackage_stdlib/fpm.toml b/example_packages/metapackage_stdlib/fpm.toml index 66df2f11fb..b90849bd50 100644 --- a/example_packages/metapackage_stdlib/fpm.toml +++ b/example_packages/metapackage_stdlib/fpm.toml @@ -1,9 +1,5 @@ name = "test_stdlib" version = "0.1.0" -license = "license" -author = "Federico Perini" -maintainer = "federico.perini@hello.world" -copyright = "Copyright 2023, Federico Perini and the fpm maintainers" [build] auto-executables = true From f8ee6f81bb13a1ad43b3ea2ec05f782eee59e079 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 11:53:18 +0200 Subject: [PATCH 337/799] remove non-mac tests --- .github/workflows/meta.yml | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 01bba6f414..8e7eca4d7c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -24,19 +24,10 @@ jobs: os: [macos-11] ##[ubuntu-latest, macos-11, windows-latest] gcc_v: [10] # Version of GFortran we want to use include: - - os: ubuntu-latest - os-arch: linux-x86_64 - release-flags: --flag '--static -g -fbacktrace -O3' - - os: macos-11 os-arch: macos-x86_64 release-flags: --flag '-g -fbacktrace -O3' - - os: windows-latest - os-arch: windows-x86_64 - release-flags: --flag '--static -g -fbacktrace -O3' - exe: .exe - env: FC: gfortran GCC_V: ${{ matrix.gcc_v }} @@ -63,22 +54,6 @@ jobs: ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib - - name: Install GFortran Linux - if: contains(matrix.os, 'ubuntu') - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \ - --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \ - --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} - - - name: Install GFortran Windows - if: contains(matrix.os, 'windows') - run: | - Invoke-WebRequest -Uri $Env:GCC_DOWNLOAD -OutFile mingw-w64.zip - Expand-Archive mingw-w64.zip - echo "$pwd\mingw-w64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - env: - GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/10.4.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-10.4.0-mingw-w64msvcrt-10.0.0-r1.zip" - # Phase 1: Bootstrap fpm with existing version - name: Install fpm uses: fortran-lang/setup-fpm@v3 From 40139bab7e5a2429f82e1170e217d3c2e308c623 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:00:32 +0200 Subject: [PATCH 338/799] setup homebrew --- .github/workflows/meta.yml | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 8e7eca4d7c..c8f816194b 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11] ##[ubuntu-latest, macos-11, windows-latest] + os: [macos-11] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 @@ -36,23 +36,9 @@ jobs: - name: Checkout code uses: actions/checkout@v1 - - name: Install GFortran macOS - if: contains(matrix.os, 'macos') - run: | - ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran - which gfortran-${GCC_V} - which gfortran - # Backport gfortran shared libraries to version 9 folder. This is necessary because all macOS releases of fpm - # have these paths hardcoded in the executable (no PIC?). As the gcc ABIs have not changed from 9 to 10, we - # can just create symbolic links for now. This can be removed when an updated fpm release is built with gcc-10 - mkdir /usr/local/opt/gcc@9 - mkdir /usr/local/opt/gcc@9/lib - mkdir /usr/local/opt/gcc@9/lib/gcc - mkdir /usr/local/opt/gcc@9/lib/gcc/9 - mkdir /usr/local/lib/gcc/9 - ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib - ln -fs /usr/local/opt/gcc@${GCC_V}/lib/gcc/${GCC_V}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib - ln -fs /usr/local/lib/gcc/${GCC_V}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib + - name: Set up Homebrew + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master # Phase 1: Bootstrap fpm with existing version - name: Install fpm From 3d49f4621ffdd0ad7bce55fa010a726da8680f6c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:05:28 +0200 Subject: [PATCH 339/799] indent --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c8f816194b..93cfeec4b3 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -37,7 +37,7 @@ jobs: uses: actions/checkout@v1 - name: Set up Homebrew - id: set-up-homebrew + id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@master # Phase 1: Bootstrap fpm with existing version From 25724f40d694d167d29bf8d9b33a3eef1914ef7a Mon Sep 17 00:00:00 2001 From: Minh Dao <43783196+minhqdao@users.noreply.github.com> Date: Fri, 21 Apr 2023 17:10:43 +0700 Subject: [PATCH 340/799] Implement `fpm publish` (#876) * Add fpm_cmd_publish module and add command with --print-request option * Add missing file * Retrieve version * Implement fpm publish --print-package-version * Use show instead of print * Parse license from manifest, add values to JSON and check if allocated * Include token * Include source-path * Use current directory as default source path * Archive package using git archive, determine available archive formats, get system tmp directory, read command line outputs * Fix path, extract name of compressed package, finalize json * Rename --show-request to --show-form-data * Include endpoint and swap base url * Use correct base url, build curl request, fix url * Check for git dependencies * Fix tests * Add docs, fix error * Remove source-path option and add cmd tests * Optionally include token with --show-form-data * Use newest version of toml-f * Add more docs * Simplify version reading * Improve docs * Nit * Fix download bug by removing forward slash * Build model again to obtain dependency tree to make sure there are no git dependencies --------- Co-authored-by: minhqdao --- app/main.f90 | 4 ++ fpm.toml | 4 +- src/fpm.f90 | 1 - src/fpm/cmd/publish.f90 | 86 ++++++++++++++++++++++++++++++++++++ src/fpm/dependency.f90 | 5 +-- src/fpm/downloader.f90 | 37 ++++++++++++++-- src/fpm/error.f90 | 2 +- src/fpm/git.f90 | 42 ++++++++++++++---- src/fpm/manifest/package.f90 | 5 +++ src/fpm/manifest/test.f90 | 2 +- src/fpm_command_line.f90 | 78 ++++++++++++++++++++++++++++---- src/fpm_filesystem.F90 | 64 ++++++++++++++++++++++++++- src/fpm_settings.f90 | 2 +- src/fpm_targets.f90 | 2 +- test/cli_test/cli_test.f90 | 36 ++++++++++++--- test/help_test/help_test.f90 | 1 + 16 files changed, 334 insertions(+), 37 deletions(-) create mode 100644 src/fpm/cmd/publish.f90 diff --git a/app/main.f90 b/app/main.f90 index 10f75b8318..95df065097 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -9,6 +9,7 @@ program main fpm_install_settings, & fpm_update_settings, & fpm_clean_settings, & + fpm_publish_settings, & get_command_line_settings use fpm_error, only: error_t use fpm_filesystem, only: exists, parent_dir, join_path @@ -16,6 +17,7 @@ program main use fpm_cmd_install, only: cmd_install use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update +use fpm_cmd_publish, only: cmd_publish use fpm_os, only: change_directory, get_current_directory implicit none @@ -80,6 +82,8 @@ program main call cmd_update(settings) type is (fpm_clean_settings) call cmd_clean(settings) +type is (fpm_publish_settings) + call cmd_publish(settings) end select if (allocated(project_root)) then diff --git a/fpm.toml b/fpm.toml index 98c6643547..dcd3f27743 100644 --- a/fpm.toml +++ b/fpm.toml @@ -11,11 +11,11 @@ macros=["FPM_RELEASE_VERSION={version}"] [dependencies] toml-f.git = "https://github.com/toml-f/toml-f" -toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" +toml-f.rev = "d7b892b1d074b7cfc5d75c3e0eb36ebc1f7958c1" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" jonquil.git = "https://github.com/toml-f/jonquil" -jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" +jonquil.rev = "4c27c8c1e411fa8790dffcf8c3fa7a27b6322273" [[test]] name = "cli-test" diff --git a/src/fpm.f90 b/src/fpm.f90 index 5247f9e58d..dcb2321493 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -7,7 +7,6 @@ module fpm fpm_run_settings, fpm_install_settings, fpm_test_settings, & fpm_clean_settings use fpm_dependency, only : new_dependency_tree -use fpm_environment, only: get_env use fpm_filesystem, only: is_dir, join_path, list_files, exists, & basename, filewrite, mkdir, run, os_delete_dir use fpm_model, only: fpm_model_t, srcfile_t, show_model, fortran_features_t, & diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 new file mode 100644 index 0000000000..8ca9a21d2d --- /dev/null +++ b/src/fpm/cmd/publish.f90 @@ -0,0 +1,86 @@ +!> Upload a package to the registry using the `publish` command. +!> +!> To upload a package you need to provide a token that will be linked to your username and created for a namespace. +!> The token can be obtained from the registry website. It can be used as `fpm publish --token `. +module fpm_cmd_publish + use fpm_command_line, only: fpm_publish_settings + use fpm_manifest, only: package_config_t, get_package_data + use fpm_model, only: fpm_model_t + use fpm_error, only: error_t, fpm_stop + use fpm_versioning, only: version_t + use fpm_filesystem, only: exists, join_path, get_tmp_directory + use fpm_git, only: git_archive, compressed_package_name + use fpm_downloader, only: downloader_t + use fpm_strings, only: string_t + use fpm_settings, only: official_registry_base_url + use fpm, only: build_model + + implicit none + private + public :: cmd_publish + +contains + + !> The `publish` command first builds the root package to obtain all the relevant information such as the + !> package version. It then creates a tarball of the package and uploads it to the registry. + subroutine cmd_publish(settings) + type(fpm_publish_settings), intent(inout) :: settings + + type(package_config_t) :: package + type(fpm_model_t) :: model + type(error_t), allocatable :: error + type(version_t), allocatable :: version + type(string_t), allocatable :: form_data(:) + character(len=:), allocatable :: tmpdir + type(downloader_t) :: downloader + integer :: i + + call get_package_data(package, 'fpm.toml', error, apply_defaults=.true.) + if (allocated(error)) call fpm_stop(1, '*cmd_build* Package error: '//error%message) + version = package%version + + ! Build model to obtain dependency tree. + call build_model(model, settings%fpm_build_settings, package, error) + if (allocated(error)) call fpm_stop(1, '*cmd_build* Model error: '//error%message) + + !> Checks before uploading the package. + if (.not. allocated(package%license)) call fpm_stop(1, 'No license specified in fpm.toml.') + if (.not. allocated(version)) call fpm_stop(1, 'No version specified in fpm.toml.') + if (version%s() == '0') call fpm_stop(1, 'Invalid version: "'//version%s()//'".') + if (.not. exists('fpm.toml')) call fpm_stop(1, "Cannot find 'fpm.toml' file. Are you in the project root?") + + ! Check if package contains git dependencies. Only publish packages without git dependencies. + do i = 1, model%deps%ndep + if (allocated(model%deps%dep(i)%git)) then + call fpm_stop(1, "Do not publish packages containing git dependencies. '"//model%deps%dep(i)%name//"' is a git dependency.") + end if + end do + + form_data = [ & + string_t('package_name="'//package%name//'"'), & + string_t('package_license="'//package%license//'"'), & + string_t('package_version="'//version%s()//'"') & + & ] + + if (allocated(settings%token)) form_data = [form_data, string_t('upload_token="'//settings%token//'"')] + + call get_tmp_directory(tmpdir, error) + if (allocated(error)) call fpm_stop(1, '*cmd_publish* Tmp directory error: '//error%message) + call git_archive('.', tmpdir, error) + if (allocated(error)) call fpm_stop(1, '*cmd_publish* Pack error: '//error%message) + form_data = [form_data, string_t('tarball=@"'//join_path(tmpdir, compressed_package_name)//'"')] + + if (settings%show_form_data) then + do i = 1, size(form_data) + print *, form_data(i)%s + end do + return + end if + + ! Make sure a token is provided for publishing. + if (.not. allocated(settings%token)) call fpm_stop(1, 'No token provided.') + + call downloader%upload_form(official_registry_base_url//'/packages', form_data, error) + if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) + end +end diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index d89b6eb836..a12078f5e4 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -668,8 +668,8 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade ! Define location of the temporary folder and file. tmp_pkg_path = join_path(global_settings%path_to_config_folder, 'tmp') - tmp_pkg_file = join_path(tmp_pkg_path, 'package_data.tmp') if (.not. exists(tmp_pkg_path)) call mkdir(tmp_pkg_path) + tmp_pkg_file = join_path(tmp_pkg_path, 'package_data.tmp') open (newunit=unit, file=tmp_pkg_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return @@ -697,7 +697,6 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (is_dir(cache_path)) call os_delete_dir(os_is_unix(), cache_path) call mkdir(cache_path) - print *, "Downloading '"//join_path(self%namespace, self%name, version%s())//"' ..." call downloader%get_file(target_url, tmp_pkg_file, error) if (allocated(error)) then close (unit, status='delete'); return @@ -782,7 +781,7 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) call fatal_error(error, "Failed to read download url for '"//join_path(node%namespace, node%name)//"'."); return end if - download_url = official_registry_base_url//'/'//download_url + download_url = official_registry_base_url//download_url if (.not. q%has_key('version')) then call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version found."); return diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 1f631ca0a0..7c5046df4e 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -3,6 +3,7 @@ module fpm_downloader use fpm_filesystem, only: which use fpm_versioning, only: version_t use jonquil, only: json_object, json_value, json_error, json_load, cast_to_object + use fpm_strings, only: string_t implicit none private @@ -12,12 +13,12 @@ module fpm_downloader !> This type could be entirely avoided but it is quite practical because it can be mocked for testing. type downloader_t contains - procedure, nopass :: get_pkg_data, get_file, unpack + procedure, nopass :: get_pkg_data, get_file, upload_form, unpack end type contains - !> Perform an http get request and save output to file. + !> Perform an http get request, save output to file, and parse json. subroutine get_pkg_data(url, version, tmp_pkg_file, json, error) character(*), intent(in) :: url type(version_t), allocatable, intent(in) :: version @@ -51,6 +52,7 @@ subroutine get_pkg_data(url, version, tmp_pkg_file, json, error) json = ptr end + !> Download a file from a url using either curl or wget. subroutine get_file(url, tmp_pkg_file, error) character(*), intent(in) :: url character(*), intent(in) :: tmp_pkg_file @@ -59,10 +61,10 @@ subroutine get_file(url, tmp_pkg_file, error) integer :: stat if (which('curl') /= '') then - print *, "Downloading package data from '"//url//"' ..." + print *, "Downloading '"//url//"' -> '"//tmp_pkg_file//"'" call execute_command_line('curl '//url//' -s -o '//tmp_pkg_file, exitstat=stat) else if (which('wget') /= '') then - print *, "Downloading package data from '"//url//"' ..." + print *, "Downloading '"//url//"' -> '"//tmp_pkg_file//"'" call execute_command_line('wget '//url//' -q -O '//tmp_pkg_file, exitstat=stat) else call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return @@ -73,6 +75,33 @@ subroutine get_file(url, tmp_pkg_file, error) end if end + !> Perform an http post request with form data. + subroutine upload_form(endpoint, form_data, error) + character(len=*), intent(in) :: endpoint + type(string_t), intent(in) :: form_data(:) + type(error_t), allocatable, intent(out) :: error + + integer :: stat, i + character(len=:), allocatable :: form_data_str + + form_data_str = '' + do i = 1, size(form_data) + form_data_str = form_data_str//"-F '"//form_data(i)%s//"' " + end do + + if (which('curl') /= '') then + print *, 'Uploading package ...' + call execute_command_line('curl -X POST -H "Content-Type: multipart/form-data" ' & + & //form_data_str//endpoint, exitstat=stat) + else + call fatal_error(error, "'curl' not installed."); return + end if + + if (stat /= 0) then + call fatal_error(error, "Error uploading package to registry."); return + end if + end + !> Unpack a tarball to a destination. subroutine unpack(tmp_pkg_file, destination, error) character(*), intent(in) :: tmp_pkg_file diff --git a/src/fpm/error.f90 b/src/fpm/error.f90 index 66bd6ee49d..f4587f654c 100644 --- a/src/fpm/error.f90 +++ b/src/fpm/error.f90 @@ -171,7 +171,7 @@ subroutine fpm_stop(value,message) flush(unit=stderr,iostat=iostat) flush(unit=stdout,iostat=iostat) if(value>0)then - write(stderr,'("",a)')trim(message) + write(stderr,'(" ",a)')trim(message) else write(stderr,'(" ",a)')trim(message) endif diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index bd0af2b443..be4b99bcf6 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -1,16 +1,14 @@ !> Implementation for interacting with git repositories. module fpm_git use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only : get_temp_filename, getline, join_path + use fpm_filesystem, only : get_temp_filename, getline, join_path, execute_and_read_output implicit none - public :: git_target_t - public :: git_target_default, git_target_branch, git_target_tag, & - & git_target_revision - public :: git_revision - public :: git_matches_manifest - public :: operator(==) - + public :: git_target_t, git_target_default, git_target_branch, git_target_tag, git_target_revision, git_revision, & + & git_archive, git_matches_manifest, operator(==), compressed_package_name + + !> Name of the compressed package that is generated temporarily. + character(len=*), parameter :: compressed_package_name = 'compressed_package' !> Possible git target type :: enum_descriptor @@ -307,5 +305,33 @@ subroutine info(self, unit, verbosity) end subroutine info + !> Archive a folder using `git archive`. + subroutine git_archive(source, destination, error) + !> Directory to archive. + character(*), intent(in) :: source + !> Destination of the archive. + character(*), intent(in) :: destination + !> Error handling. + type(error_t), allocatable, intent(out) :: error + + integer :: stat + character(len=:), allocatable :: cmd_output, archive_format + + call execute_and_read_output('git archive -l', cmd_output, error) + if (allocated(error)) return + + if (index(cmd_output, 'tar.gz') /= 0) then + archive_format = 'tar.gz' + else + call fatal_error(error, "Cannot find a suitable archive format for 'git archive'."); return + end if + + call execute_command_line('git archive HEAD --format='//archive_format//' -o '// & + & join_path(destination, compressed_package_name), exitstat=stat) + if (stat /= 0) then + call fatal_error(error, "Error packing '"//source//"'."); return + end if + end + end module fpm_git diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index e966bfa461..ddad144d75 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -80,6 +80,9 @@ module fpm_manifest_package !> Fortran meta data type(fortran_config_t) :: fortran + !> License meta data + character(len=:), allocatable :: license + !> Library meta data type(library_config_t), allocatable :: library @@ -151,6 +154,8 @@ subroutine new_package(self, table, root, error) return endif + call get_value(table, "license", self%license) + if (len(self%name) <= 0) then call syntax_error(error, "Package name must be a non-empty string") return diff --git a/src/fpm/manifest/test.f90 b/src/fpm/manifest/test.f90 index c82212ebea..7d0ac78a15 100644 --- a/src/fpm/manifest/test.f90 +++ b/src/fpm/manifest/test.f90 @@ -15,7 +15,7 @@ !>[test.dependencies] !>``` module fpm_manifest_test - use fpm_manifest_dependency, only : dependency_config_t, new_dependencies + use fpm_manifest_dependency, only : new_dependencies use fpm_manifest_executable, only : executable_config_t use fpm_error, only : error_t, syntax_error, bad_name_error use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 867eecb76a..0a68e501b1 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -49,6 +49,7 @@ module fpm_command_line fpm_test_settings, & fpm_update_settings, & fpm_clean_settings, & + fpm_publish_settings, & get_command_line_settings type, abstract :: fpm_cmd_settings @@ -117,6 +118,12 @@ module fpm_command_line logical :: clean_call=.false. end type +type, extends(fpm_build_settings) :: fpm_publish_settings + logical :: show_package_version = .false. + logical :: show_form_data = .false. + character(len=:), allocatable :: token +end type + character(len=:),allocatable :: name character(len=:),allocatable :: os_type character(len=ibug),allocatable :: names(:) @@ -127,10 +134,10 @@ module fpm_command_line & help_test(:), help_build(:), help_usage(:), help_runner(:), & & help_text(:), help_install(:), help_help(:), help_update(:), & & help_list(:), help_list_dash(:), help_list_nodash(:), & - & help_clean(:) + & help_clean(:), help_publish(:) character(len=20),parameter :: manual(*)=[ character(len=20) ::& & ' ', 'fpm', 'new', 'build', 'run', 'clean', & -& 'test', 'runner', 'install', 'update', 'list', 'help', 'version' ] +& 'test', 'runner', 'install', 'update', 'list', 'help', 'version', 'publish' ] character(len=:), allocatable :: val_runner, val_compiler, val_flag, val_cflag, val_cxxflag, val_ldflag, & val_profile @@ -211,6 +218,7 @@ subroutine get_command_line_settings(cmd_settings) integer :: os logical :: unix type(fpm_install_settings), allocatable :: install_settings + type(fpm_publish_settings), allocatable :: publish_settings type(version_t) :: version character(len=:), allocatable :: common_args, compiler_args, run_args, working_dir, & & c_compiler, cxx_compiler, archiver, version_s @@ -481,6 +489,8 @@ subroutine get_command_line_settings(cmd_settings) help_text=[character(len=widest) :: help_text, version_text] case('clean' ) help_text=[character(len=widest) :: help_text, help_clean] + case('publish') + help_text=[character(len=widest) :: help_text, help_publish] case default help_text=[character(len=widest) :: help_text, & & ' unknown help topic "'//trim(unnamed(i))//'"'] @@ -608,6 +618,42 @@ subroutine get_command_line_settings(cmd_settings) & clean_skip=lget('skip'), & clean_call=lget('all')) + case('publish') + call set_args(common_args // compiler_args //'& + & --show-package-version F & + & --show-form-data F & + & --token " " & + & --list F & + & --show-model F & + & --tests F & + & --', help_publish, version_text) + + call check_build_vals() + + c_compiler = sget('c-compiler') + cxx_compiler = sget('cxx-compiler') + archiver = sget('archiver') + + allocate(publish_settings, source=fpm_publish_settings( & + & show_package_version = lget('show-package-version'), & + & show_form_data = lget('show-form-data'), & + & profile=val_profile,& + & prune=.not.lget('no-prune'), & + & compiler=val_compiler, & + & c_compiler=c_compiler, & + & cxx_compiler=cxx_compiler, & + & archiver=archiver, & + & flag=val_flag, & + & cflag=val_cflag, & + & cxxflag=val_cxxflag, & + & ldflag=val_ldflag, & + & list=lget('list'),& + & show_model=lget('show-model'),& + & build_tests=lget('tests'),& + & verbose=lget('verbose'))) + call get_char_arg(publish_settings%token, 'token') + call move_alloc(publish_settings, cmd_settings) + case default if(cmdarg.ne.''.and.which('fpm-'//cmdarg).ne.'')then @@ -643,12 +689,8 @@ subroutine get_command_line_settings(cmd_settings) contains subroutine check_build_vals() - character(len=:), allocatable :: flags - val_compiler=sget('compiler') - if(val_compiler=='') then - val_compiler='gfortran' - endif + if(val_compiler=='') val_compiler='gfortran' val_flag = " " // sget('flag') val_cflag = " " // sget('c-flag') @@ -691,6 +733,7 @@ subroutine set_help() ' update Update and manage project dependencies ', & ' install Install project ', & ' clean Delete the build ', & + ' publish Publish package to the registry ', & ' ', & ' Enter "fpm --list" for a brief list of subcommand options. Enter ', & ' "fpm --help" or "fpm SUBCOMMAND --help" for detailed descriptions. ', & @@ -711,6 +754,7 @@ subroutine set_help() ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & ' clean [--skip] [--all] ', & + ' publish [--show-package-version] [--show-form-data] [--token TOKEN] ', & ' '] help_usage=[character(len=80) :: & '' ] @@ -815,6 +859,7 @@ subroutine set_help() ' + install Install project. ', & ' + clean Delete directories in the "build/" directory, except ', & ' dependencies. Prompts for confirmation to delete. ', & + ' + publish Publish package to the registry. ', & ' ', & ' Their syntax is ', & ' ', & @@ -832,7 +877,8 @@ subroutine set_help() ' list [--list] ', & ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & - ' clean [--skip] [--all] ', & + ' clean [--skip] [--all] ', & + ' publish [--show-package-version] [--show-form-data] [--token TOKEN] ', & ' ', & 'SUBCOMMAND OPTIONS ', & ' -C, --directory PATH', & @@ -1307,6 +1353,22 @@ subroutine set_help() 'OPTIONS', & ' --skip delete the build without prompting but skip dependencies.', & ' --all delete the build without prompting including dependencies.', & + '' ] + help_publish=[character(len=80) :: & + 'NAME', & + ' publish(1) - publish package to the registry', & + '', & + 'SYNOPSIS', & + ' fpm publish [--token TOKEN]', & + '', & + 'DESCRIPTION', & + ' Collect relevant source files and upload package to the registry.', & + ' It is mandatory to provide a token. The token can be generated on the', & + ' registry website and will be linked to your username and namespace.', & + '', & + 'OPTIONS', & + ' --show-package-version show package version without publishing', & + ' --show-form-data show sent form data without publishing', & '' ] end subroutine set_help diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 3846654354..b192107afc 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -14,7 +14,8 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home + LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, get_tmp_directory, & + execute_and_read_output integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -1020,4 +1021,65 @@ subroutine get_home(home, error) end if end subroutine get_home + !> Execute command line and return output as a string. + subroutine execute_and_read_output(cmd, output, error, exitstat) + !> Command to execute. + character(len=*), intent(in) :: cmd + !> Command line output. + character(len=:), allocatable, intent(out) :: output + !> Error to handle. + type(error_t), allocatable, intent(out) :: error + !> Can optionally used for error handling. + integer, intent(out), optional :: exitstat + + integer :: cmdstat, unit, stat = 0 + character(len=:), allocatable :: cmdmsg, tmp_path + character(len=1000) :: output_line + + call get_tmp_directory(tmp_path, error) + if (allocated(error)) return + + if (.not. exists(tmp_path)) call mkdir(tmp_path) + tmp_path = join_path(tmp_path, 'command_line_output') + call delete_file(tmp_path) + call filewrite(tmp_path, ['']) + + call execute_command_line(cmd//' > '//tmp_path, exitstat=exitstat, cmdstat=cmdstat) + if (cmdstat /= 0) call fpm_stop(1,'*run*: '//"Command failed: '"//cmd//"'. Message: '"//trim(cmdmsg)//"'.") + + open(unit, file=tmp_path, action='read', status='old') + output = '' + do + read(unit, *, iostat=stat) output_line + if (stat /= 0) exit + output = output//trim(output_line)//' ' + end do + close(unit, status='delete') + end + + !> Get system-dependent tmp directory. + subroutine get_tmp_directory(tmp_dir, error) + !> System-dependant tmp directory. + character(len=:), allocatable, intent(out) :: tmp_dir + !> Error to handle. + type(error_t), allocatable, intent(out) :: error + + tmp_dir = get_env('TMPDIR', '') + if (tmp_dir /= '') then + tmp_dir = tmp_dir//'fpm'; return + end if + + tmp_dir = get_env('TMP', '') + if (tmp_dir /= '') then + tmp_dir = tmp_dir//'fpm'; return + end if + + tmp_dir = get_env('TEMP', '') + if (tmp_dir /= '') then + tmp_dir = tmp_dir//'fpm'; return + end if + + call fatal_error(error, "Couldn't determine system temporary directory.") + end + end module fpm_filesystem diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index cc53df2f7d..75fbb21d2b 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -10,7 +10,7 @@ module fpm_settings private public :: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url - character(*), parameter :: official_registry_base_url = 'https://fpm-registry.onrender.com' + character(*), parameter :: official_registry_base_url = 'https://registry-apis.vercel.app' type :: fpm_global_settings !> Path to the global config file excluding the file name. diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index ddd34cd7d4..ae1f120296 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -146,7 +146,7 @@ subroutine targets_from_sources(targets,model,prune,error) !> Enable tree-shaking/pruning of module dependencies logical, intent(in) :: prune - + !> Error structure type(error_t), intent(out), allocatable :: error diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index 9f82cb7056..69fd433145 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -29,10 +29,13 @@ program main logical :: w_t,act_w_t ; namelist/act_cli/act_w_t logical :: c_s,act_c_s ; namelist/act_cli/act_c_s logical :: c_a,act_c_a ; namelist/act_cli/act_c_a +logical :: show_v,act_show_v ; namelist/act_cli/act_show_v +logical :: show_f_d,act_show_f_d; namelist/act_cli/act_show_f_d +character(len=:), allocatable :: token, act_token ; namelist/act_cli/act_token -character(len=63) :: profile,act_profile ; namelist/act_cli/act_profile -character(len=:),allocatable :: args,act_args ; namelist/act_cli/act_args -namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args +character(len=:), allocatable :: profile,act_profile ; namelist/act_cli/act_profile +character(len=:), allocatable :: args,act_args ; namelist/act_cli/act_args +namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args,show_v,show_f_d,token integer :: lun logical,allocatable :: tally(:) logical,allocatable :: subtally(:) @@ -70,7 +73,10 @@ program main 'CMD="clean", NAME= ARGS="",', & 'CMD="clean --skip", C_S=T, NAME= ARGS="",', & -'CMD="clean --all", C_A=T, NAME= ARGS="",', & +'CMD="clean --all", C_A=T, NAME= ARGS="",', & +'CMD="publish --token abc --show-package-version", SHOW_V=T, NAME= token="abc",ARGS="",', & +'CMD="publish --token abc --show-form-data", SHOW_F_D=T, NAME= token="abc",ARGS="",', & +'CMD="publish --token abc", NAME= token="abc",ARGS="",', & ' ' ] character(len=256) :: readme(3) @@ -98,11 +104,14 @@ program main endif ! blank out name group EXPECTED name=[(repeat(' ',len(name)),i=1,max_names)] ! the words on the command line sans the subcommand name - profile="" ! --profile PROF + profile='' ! --profile PROF w_e=.false. ! --app w_t=.false. ! --test c_s=.false. ! --skip c_a=.false. ! --all + show_v=.false. ! --show-package-version + show_f_d=.false. ! --show-form-data + token='' ! --token TOKEN args=repeat(' ',132) ! -- ARGS cmd=repeat(' ',132) ! the command line arguments to test cstat=0 ! status values from EXECUTE_COMMAND_LINE() @@ -122,6 +131,9 @@ program main act_w_t=.false. act_c_s=.false. act_c_a=.false. + act_show_v=.false. + act_show_f_d=.false. + act_token='' act_args=repeat(' ',132) read(lun,nml=act_cli,iostat=ios,iomsg=message) if(ios/=0)then @@ -135,6 +147,9 @@ program main call test_test('WITH_EXPECTED',act_w_e.eqv.w_e) call test_test('WITH_TESTED',act_w_t.eqv.w_t) call test_test('WITH_TEST',act_w_t.eqv.w_t) + call test_test('SHOW-PACKAGE-VERSION',act_show_v.eqv.show_v) + call test_test('SHOW-FORM-DATA',act_show_f_d.eqv.show_f_d) + call test_test('TOKEN',act_token==token) call test_test('ARGS',act_args==args) if(all(subtally))then write(*,'(*(g0))')'PASSED: TEST ',i,' STATUS: expected ',cstat,' ',estat,' actual ',act_cstat,' ',act_estat,& @@ -205,10 +220,12 @@ subroutine parse() fpm_test_settings, & fpm_clean_settings, & fpm_install_settings, & - get_command_line_settings + get_command_line_settings, & + fpm_publish_settings use fpm, only: cmd_run, cmd_clean use fpm_cmd_install, only: cmd_install use fpm_cmd_new, only: cmd_new +use fpm_cmd_publish, only: cmd_publish class(fpm_cmd_settings), allocatable :: cmd_settings ! duplicates the calls as seen in the main program for fpm call get_command_line_settings(cmd_settings) @@ -219,6 +236,9 @@ subroutine parse() act_w_t=.false. act_c_s=.false. act_c_a=.false. +act_show_v=.false. +act_show_f_d=.false. +act_token='' act_profile='' select type(settings=>cmd_settings) @@ -240,6 +260,10 @@ subroutine parse() act_c_s=settings%clean_skip act_c_a=settings%clean_call type is (fpm_install_settings) +type is (fpm_publish_settings) + act_show_v=settings%show_package_version + act_show_f_d=settings%show_form_data + act_token=settings%token end select open(file='_test_cli',newunit=lun,delim='quote') diff --git a/test/help_test/help_test.f90 b/test/help_test/help_test.f90 index e78a4ea788..8112b81a37 100644 --- a/test/help_test/help_test.f90 +++ b/test/help_test/help_test.f90 @@ -34,6 +34,7 @@ program help_test 'help list >> fpm_scratch_help.txt',& 'help help >> fpm_scratch_help.txt',& 'help clean >> fpm_scratch_help.txt',& +'help publish >> fpm_scratch_help.txt',& '--version >> fpm_scratch_help.txt',& ! generate manual ' help manual > fpm_scratch_manual.txt'] From 4dbf0064f1b92f37c754a5069380815ee2dcae12 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:17:34 +0200 Subject: [PATCH 341/799] install gcc --- .github/workflows/meta.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 93cfeec4b3..af1f3f3316 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -40,6 +40,12 @@ jobs: id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@master + - name: Install Homebrew gfortran + if: matrix.os.contains('macos') + shell: bash + run: brew install gcc@${{ GCC_V }} + + # Phase 1: Bootstrap fpm with existing version - name: Install fpm uses: fortran-lang/setup-fpm@v3 From 611a6d7b444dc4362b87ca83ee74884092178696 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:19:17 +0200 Subject: [PATCH 342/799] Update meta.yml --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index af1f3f3316..1fbf23a5e9 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -43,7 +43,7 @@ jobs: - name: Install Homebrew gfortran if: matrix.os.contains('macos') shell: bash - run: brew install gcc@${{ GCC_V }} + run: brew install gcc@${GCC_V} # Phase 1: Bootstrap fpm with existing version From aea4ef0893bb604b318e739e51f018add9f4b30e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:21:14 +0200 Subject: [PATCH 343/799] Update meta.yml --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 1fbf23a5e9..ed2b28d63d 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -41,7 +41,7 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Install Homebrew gfortran - if: matrix.os.contains('macos') + if: contains(matrix.os, 'macos') shell: bash run: brew install gcc@${GCC_V} From fbd312bba5904f971dfddeb1d0b8810d2c053fc3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:25:50 +0200 Subject: [PATCH 344/799] check gfortran gfortran-10 --- .github/workflows/meta.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index ed2b28d63d..c294598bdc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -44,6 +44,8 @@ jobs: if: contains(matrix.os, 'macos') shell: bash run: brew install gcc@${GCC_V} + which gfortran-${GCC_V} + which gfortran # Phase 1: Bootstrap fpm with existing version From 3d967b791cbf5ce3681ff6c92afa2a9716edce21 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:29:20 +0200 Subject: [PATCH 345/799] Update meta.yml --- .github/workflows/meta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c294598bdc..4d6640053c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -42,8 +42,8 @@ jobs: - name: Install Homebrew gfortran if: contains(matrix.os, 'macos') - shell: bash - run: brew install gcc@${GCC_V} + run: | + brew install gcc@${GCC_V} which gfortran-${GCC_V} which gfortran From 5a229800091df4ae52968c8217099fbdeac58f63 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:35:42 +0200 Subject: [PATCH 346/799] install mpich --- .github/workflows/meta.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 4d6640053c..558cd65bbc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -21,7 +21,8 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11] + os: [macos-11] + mpi: [mpich] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 @@ -44,9 +45,14 @@ jobs: if: contains(matrix.os, 'macos') run: | brew install gcc@${GCC_V} + ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran which gfortran-${GCC_V} which gfortran - + + - name: Install homebrew MPICH + if: contains(matrix.mpi,'mpich') + run: | + brew install mpich # Phase 1: Bootstrap fpm with existing version - name: Install fpm From 5e6f3b6e0add539927f52e53aaefa9c3cbfc5a9a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 12:44:03 +0200 Subject: [PATCH 347/799] add homebrew OpenMPI case --- .github/workflows/meta.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 558cd65bbc..e17805d479 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [macos-11] - mpi: [mpich] + mpi: [mpich,openmpi] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 @@ -54,6 +54,11 @@ jobs: run: | brew install mpich + - name: Install homebrew OpenMPI + if: contains(matrix.mpi,'openmpi') + run: | + brew install --cc=gcc-${GCC_V} openmpi + # Phase 1: Bootstrap fpm with existing version - name: Install fpm uses: fortran-lang/setup-fpm@v3 From 1406197a1045520b007e049ddc963ffe20177f14 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 13:00:59 +0200 Subject: [PATCH 348/799] add windows image --- .github/workflows/meta.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index e17805d479..5154f08db6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -21,13 +21,17 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11] + os: [macos-11,windows-2019] mpi: [mpich,openmpi] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 os-arch: macos-x86_64 release-flags: --flag '-g -fbacktrace -O3' + - os: windows-2019 + os-arch: windows-x86_64 + release-flags: --flag '--static -g -fbacktrace -O3' + exe: .exe env: FC: gfortran @@ -37,7 +41,19 @@ jobs: - name: Checkout code uses: actions/checkout@v1 + - name: Set up MSYS2 and gfortran + uses: msys2/setup-msys2@v2 + if: contains(matrix.os,'windows') + with: + msystem: MINGW64 + update: false + install: >- + wget + unzip + gcc-fortran + - name: Set up Homebrew + if: contains(matrix.os,'macos') id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@master From 9e6d0eccf24f2e0a418f85c0f754277da1611ac1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 13:06:46 +0200 Subject: [PATCH 349/799] add MSMPI type --- .github/workflows/meta.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 5154f08db6..a7ecb74778 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [macos-11,windows-2019] - mpi: [mpich,openmpi] + mpi: [mpich,openmpi,msmpi] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 @@ -51,6 +51,7 @@ jobs: wget unzip gcc-fortran + msmpi - name: Set up Homebrew if: contains(matrix.os,'macos') @@ -67,14 +68,21 @@ jobs: - name: Install homebrew MPICH if: contains(matrix.mpi,'mpich') + if: contains(matrix.os,'macos') run: | brew install mpich - name: Install homebrew OpenMPI if: contains(matrix.mpi,'openmpi') + if: contains(matrix.os,'macos') run: | brew install --cc=gcc-${GCC_V} openmpi + - name: Install MSYS2 MSMPI + if: contains(matrix.mpi,'msmpi') + if: contains(matrix.os,'windows') + run: | + # Phase 1: Bootstrap fpm with existing version - name: Install fpm uses: fortran-lang/setup-fpm@v3 From 5785c3de139025581d91303eb3b600498697175b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 13:20:39 +0200 Subject: [PATCH 350/799] duplicate ifs --- .github/workflows/meta.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index a7ecb74778..5f2f770b6f 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -67,20 +67,17 @@ jobs: which gfortran - name: Install homebrew MPICH - if: contains(matrix.mpi,'mpich') - if: contains(matrix.os,'macos') + if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') run: | brew install mpich - name: Install homebrew OpenMPI - if: contains(matrix.mpi,'openmpi') - if: contains(matrix.os,'macos') + if: contains(matrix.mpi,'openmpi') && contains(matrix.os,'macos') run: | brew install --cc=gcc-${GCC_V} openmpi - name: Install MSYS2 MSMPI - if: contains(matrix.mpi,'msmpi') - if: contains(matrix.os,'windows') + if: contains(matrix.mpi,'msmpi') && contains(matrix.os,'windows') run: | # Phase 1: Bootstrap fpm with existing version From bf221729d7c1b3fd0021dfcd368d1bfc4c9988e0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 13:36:20 +0200 Subject: [PATCH 351/799] reduce matrix --- .github/workflows/meta.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 5f2f770b6f..fdf7565eea 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -21,15 +21,19 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11,windows-2019] - mpi: [mpich,openmpi,msmpi] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 os-arch: macos-x86_64 + mpi: openmpi + release-flags: --flag '-g -fbacktrace -O3' + - os: macos-11 + os-arch: macos-x86_64 + mpi: mpich release-flags: --flag '-g -fbacktrace -O3' - os: windows-2019 os-arch: windows-x86_64 + mpi: msmpi release-flags: --flag '--static -g -fbacktrace -O3' exe: .exe From 7bff6fa80a027db1b547747dc91a518bda601d2b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 13:58:30 +0200 Subject: [PATCH 352/799] Revert "reduce matrix" This reverts commit bf221729d7c1b3fd0021dfcd368d1bfc4c9988e0. --- .github/workflows/meta.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index fdf7565eea..5f2f770b6f 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -21,19 +21,15 @@ jobs: strategy: fail-fast: false matrix: + os: [macos-11,windows-2019] + mpi: [mpich,openmpi,msmpi] gcc_v: [10] # Version of GFortran we want to use include: - os: macos-11 os-arch: macos-x86_64 - mpi: openmpi - release-flags: --flag '-g -fbacktrace -O3' - - os: macos-11 - os-arch: macos-x86_64 - mpi: mpich release-flags: --flag '-g -fbacktrace -O3' - os: windows-2019 os-arch: windows-x86_64 - mpi: msmpi release-flags: --flag '--static -g -fbacktrace -O3' exe: .exe From 53a20960480594dade1218c3a4ff3b5885ff2447 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 13:59:42 +0200 Subject: [PATCH 353/799] reduce matrix --- .github/workflows/meta.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 5f2f770b6f..a58052883e 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -24,6 +24,13 @@ jobs: os: [macos-11,windows-2019] mpi: [mpich,openmpi,msmpi] gcc_v: [10] # Version of GFortran we want to use + exclude: + - os: macos-11 + mpi: msmpi + - os: windows-2019 + mpi: mpich + - os: windows-2019 + mpi: openmpi include: - os: macos-11 os-arch: macos-x86_64 From aaa519529bedfd74e5ea2ffa40b423980584390e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:04:40 +0200 Subject: [PATCH 354/799] improve windows CI --- .github/workflows/meta.yml | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index a58052883e..75ea7f4460 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -48,17 +48,38 @@ jobs: - name: Checkout code uses: actions/checkout@v1 - - name: Set up MSYS2 and gfortran - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@v2 if: contains(matrix.os,'windows') with: msystem: MINGW64 - update: false + update: true install: >- - wget - unzip - gcc-fortran - msmpi + mingw-w64-x86_64-wget + mingw-w64-x86_64-unzip + mingw-w64-x86_64-gcc-fortran + mingw-w64-x86_64-msmpi + + - name: Put MSYS2_MinGW64 on PATH + if: contains(matrix.os,'windows') + # there is not yet an environment variable for this path from msys2/setup-msys2 + run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: download MS-MPI setup (SDK is from MSYS2) + if: contains(matrix.os,'windows') + run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.1/msmpisetup.exe + + - name: Install mpiexec.exe (-force needed to bypass GUI on headless) + if: contains(matrix.os,'windows') + run: .\msmpisetup.exe -unattend -force + + - name: test that mpiexec.exe exists + if: contains(matrix.os,'windows') + # can't use MSMPI_BIN as Actions doesn't update PATH from msmpisetup.exe + run: Test-Path "C:\Program Files\Microsoft MPI\Bin\mpiexec.exe" -PathType leaf + + - name: put MSMPI_BIN on PATH (where mpiexec is) + if: contains(matrix.os,'windows') + run: echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Set up Homebrew if: contains(matrix.os,'macos') From 1309f501619c007bd1140f5686dd31dd90762476 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:08:13 +0200 Subject: [PATCH 355/799] Update meta.yml --- .github/workflows/meta.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 75ea7f4460..b19ae577f6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -54,10 +54,11 @@ jobs: msystem: MINGW64 update: true install: >- - mingw-w64-x86_64-wget - mingw-w64-x86_64-unzip - mingw-w64-x86_64-gcc-fortran - mingw-w64-x86_64-msmpi + wget + unzip + curl + gcc-fortran + msmpi - name: Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') From b7879bfe1556d8fa6fbfeab00eb978305c8f0832 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:14:14 +0200 Subject: [PATCH 356/799] use windows-latest --- .github/workflows/meta.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index b19ae577f6..0866a44c49 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -21,21 +21,21 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11,windows-2019] + os: [macos-11,windows-latest] mpi: [mpich,openmpi,msmpi] gcc_v: [10] # Version of GFortran we want to use exclude: - os: macos-11 mpi: msmpi - - os: windows-2019 + - os: windows-latest mpi: mpich - - os: windows-2019 + - os: windows-latest mpi: openmpi include: - os: macos-11 os-arch: macos-x86_64 release-flags: --flag '-g -fbacktrace -O3' - - os: windows-2019 + - os: windows-latest os-arch: windows-x86_64 release-flags: --flag '--static -g -fbacktrace -O3' exe: .exe From 391b4058ba26ed51a4a49a3f5e57207dc25ad4fb Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:27:25 +0200 Subject: [PATCH 357/799] Update meta.yml --- .github/workflows/meta.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 0866a44c49..ea3aef00b6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -54,6 +54,8 @@ jobs: msystem: MINGW64 update: true install: >- + git + base-devel wget unzip curl From 20c6cbaf959079953945b069b3835e9948959ea0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:35:31 +0200 Subject: [PATCH 358/799] change order --- .github/workflows/meta.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index ea3aef00b6..26c26f9f54 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -60,7 +60,6 @@ jobs: unzip curl gcc-fortran - msmpi - name: Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') @@ -84,6 +83,10 @@ jobs: if: contains(matrix.os,'windows') run: echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Install MSYS2 msmpi package + if: contains(matrix.os,'windows') + run: pacman -Ss msmpi + - name: Set up Homebrew if: contains(matrix.os,'macos') id: set-up-homebrew From 85e3565b68d95f94229a706b606393c0fc0d03de Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:44:13 +0200 Subject: [PATCH 359/799] install msmpi --- .github/workflows/meta.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 26c26f9f54..435256db84 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -85,7 +85,8 @@ jobs: - name: Install MSYS2 msmpi package if: contains(matrix.os,'windows') - run: pacman -Ss msmpi + shell: msys2 {0} + run: pacman --noconfirm -S mingw-w64-x86_64-msmpi - name: Set up Homebrew if: contains(matrix.os,'macos') From 7518c81a1f7c39931385c639d45ad360af83c202 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 14:54:17 +0200 Subject: [PATCH 360/799] fix $PATH --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 435256db84..c26860a7de 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -64,7 +64,7 @@ jobs: - name: Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 - run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $GITHUB_PATH -Encoding utf8 -Append - name: download MS-MPI setup (SDK is from MSYS2) if: contains(matrix.os,'windows') From 3e5de3705c5a5d37f7c312179257038cad1a706d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 15:07:49 +0200 Subject: [PATCH 361/799] fix path --- .github/workflows/meta.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c26860a7de..126d5603e5 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -64,7 +64,7 @@ jobs: - name: Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 - run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $GITHUB_PATH -Encoding utf8 -Append + run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: download MS-MPI setup (SDK is from MSYS2) if: contains(matrix.os,'windows') @@ -111,10 +111,6 @@ jobs: run: | brew install --cc=gcc-${GCC_V} openmpi - - name: Install MSYS2 MSMPI - if: contains(matrix.mpi,'msmpi') && contains(matrix.os,'windows') - run: | - # Phase 1: Bootstrap fpm with existing version - name: Install fpm uses: fortran-lang/setup-fpm@v3 From 87ed9c62e7892b1683c1775e7f579120d1c9bb4b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 15:10:58 +0200 Subject: [PATCH 362/799] search in Windows %PATH% --- src/fpm_meta.f90 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index b64bc28529..ffce5af08a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -554,8 +554,15 @@ logical function msmpi_init(this,compiler,error) result(found) if (allocated(error)) return bindir = get_env('MSMPI_BIN') - if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then - call fatal_error(error,'MS-MPI error: MS-MPI Runtime directory is missing. check environment variable %MSMPI_BIN%.') + + ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). + ! Do a second attempt: search for mpiexec.exe + if (len_trim(bindir)<=0 .or. .not.exists(bindir)) & + call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) + + if (allocated(error) .or. len_trim(bindir)<=0 .or. .not.exists(bindir)) then + call fatal_error(error,'MS-MPI error: MS-MPI Runtime directory is missing. '//& + 'check environment variable %MSMPI_BIN% or that the folder is in %PATH%.') return end if From 6d1a6ece9932f0d0bded46b6c99061fa6b02341a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 15:22:06 +0200 Subject: [PATCH 363/799] do not look for msmpi.dll --- src/fpm_meta.f90 | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index ffce5af08a..b8d1a02cd7 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -549,10 +549,6 @@ logical function msmpi_init(this,compiler,error) result(found) end if ! Check that the runtime is installed - windir = get_env('WINDIR') - call get_absolute_path(join_path(windir,'system32\msmpi.dll'),libdir,error) - if (allocated(error)) return - bindir = get_env('MSMPI_BIN') ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). @@ -566,11 +562,6 @@ logical function msmpi_init(this,compiler,error) result(found) return end if - if (len_trim(libdir)<=0 .or. .not.exists(libdir)) then - call fatal_error(error,'MS-MPI error: msmpi.dll is missing. Is MS-MPI installed on this system?') - return - end if - ! Success! found = .true. From 918336d9a97f89bfb87f5a21ee8fa81ada12632b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 15:29:46 +0200 Subject: [PATCH 364/799] fix bindir --- src/fpm_meta.f90 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index b8d1a02cd7..e69d4a1dd0 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -553,10 +553,12 @@ logical function msmpi_init(this,compiler,error) result(found) ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). ! Do a second attempt: search for mpiexec.exe - if (len_trim(bindir)<=0 .or. .not.exists(bindir)) & - call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) + if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then + call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) + if (allocated(error)) return + endif - if (allocated(error) .or. len_trim(bindir)<=0 .or. .not.exists(bindir)) then + if (allocated(error) .or. len_trim(bindir)<=0) then call fatal_error(error,'MS-MPI error: MS-MPI Runtime directory is missing. '//& 'check environment variable %MSMPI_BIN% or that the folder is in %PATH%.') return @@ -654,7 +656,7 @@ logical function msmpi_init(this,compiler,error) result(found) !> Add default run command this%has_run_command = .true. - this%run_command = string_t(join_path(get_dos_path(bindir,error),'mpiexec')//' -np * ') + this%run_command = string_t(join_path(get_dos_path(bindir,error),'mpiexec.exe')//' -np * ') else From a3184ad3cc4a67ba02dd7fb1ade02f408dddeb22 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 15:46:50 +0200 Subject: [PATCH 365/799] output path in case it is not a dir --- src/fpm_meta.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e69d4a1dd0..4971c76256 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -741,7 +741,7 @@ subroutine find_command_location(command,path,echo,verbose,error) end if if (.not.is_dir(path)) then - call fatal_error(error,'full path to command ('//command//') is not a directory') + call fatal_error(error,'full path ('//path//') to command ('//command//') is not a directory') return end if From 847765d4d6808456f839d4e7665f463885d9239e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 16:05:20 +0200 Subject: [PATCH 366/799] do not use `canon_path`: returns inverse slashes on WSL! --- src/fpm_meta.f90 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 4971c76256..9c38041d52 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -669,6 +669,15 @@ logical function msmpi_init(this,compiler,error) result(found) end function msmpi_init +!> Check if we're under a WSL bash shell +logical function wsl_shell() + if (get_os_type()==OS_WINDOWS) then + wsl_shell = exists('/proc/sys/fs/binfmt_misc/WSLInterop') + else + wsl_shell = .false. + endif +end function wsl_shell + !> Find the location of a valid command subroutine find_command_location(command,path,echo,verbose,error) character(*), intent(in) :: command @@ -737,7 +746,7 @@ subroutine find_command_location(command,path,echo,verbose,error) ! Compiler is in the current folder call get_absolute_path('.',path,error) else - path = canon_path(fullpath(1:length-1)) + call get_absolute_path(fullpath(1:length-1),path,error) end if if (.not.is_dir(path)) then From 1e6f8bca3328ec3c09a36ba44a6928511ba840da Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 09:35:38 -0500 Subject: [PATCH 367/799] fix WSL paths --- src/fpm_meta.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 9c38041d52..c70a082de6 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -558,7 +558,7 @@ logical function msmpi_init(this,compiler,error) result(found) if (allocated(error)) return endif - if (allocated(error) .or. len_trim(bindir)<=0) then + if (allocated(error) .or. .not.exists(bindir)) then call fatal_error(error,'MS-MPI error: MS-MPI Runtime directory is missing. '//& 'check environment variable %MSMPI_BIN% or that the folder is in %PATH%.') return @@ -748,6 +748,10 @@ subroutine find_command_location(command,path,echo,verbose,error) else call get_absolute_path(fullpath(1:length-1),path,error) end if + if (allocated(error)) return + + ! On Windows, be sure to return a path with no spaces + if (get_os_type()==OS_WINDOWS) path = get_dos_path(path,error) if (.not.is_dir(path)) then call fatal_error(error,'full path ('//path//') to command ('//command//') is not a directory') From e9a4ceb63f7fd7c54e1a58f98c8dfccc02d8480d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 10:01:42 -0500 Subject: [PATCH 368/799] Only launch job if any metapackage source files have changed --- .github/workflows/meta.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 126d5603e5..a7561abdbc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -2,6 +2,10 @@ name: metapackage-tests on: push: + paths: + - 'src/*meta*.f90' # On push, only launch job if something has changed in the metapackages + - 'src/fpm/*meta*.f90' + - 'src/fpm/manifest/*meta*.f90' pull_request: release: types: [published] From 43b670e085a1322a2616b2e77ac3bf3453535947 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 17:31:36 +0200 Subject: [PATCH 369/799] remove unnecessary verbosity --- src/fpm_meta.f90 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c70a082de6..c414127a59 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -93,10 +93,8 @@ module fpm_meta integer, parameter :: MPI_TYPE_INTEL = 3 integer, parameter :: MPI_TYPE_MSMPI = 4 - - !> Debugging information -logical, parameter, private :: verbose = .true. +logical, parameter, private :: verbose = .false. integer, parameter, private :: WRAPPER_FORTRAN = 1 integer, parameter, private :: WRAPPER_C = 2 @@ -772,7 +770,7 @@ subroutine get_mpi_runner(command,verbose,error) ! Try several commands do itri=1,size(try) - call find_command_location(trim(try(itri)),command%s,verbose=verbose,error=error) + call find_command_location(trim(try(itri)),command%s,echo=.false.,verbose=verbose,error=error) ! Success! success = len_trim(command%s)>0 .and. .not.allocated(error) @@ -795,7 +793,7 @@ subroutine compiler_get_path(self,path,error) type(string_t), intent(out) :: path type(error_t), allocatable, intent(out) :: error - call find_command_location(self%fc,path%s,self%echo,self%verbose,error) + call find_command_location(self%fc,path%s,.false.,self%verbose,error) end subroutine compiler_get_path From 8d6cc28f5cc85d8cee7638b26337884c5c9c196d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 11:24:57 -0500 Subject: [PATCH 370/799] Revert "remove unnecessary verbosity" This reverts commit 43b670e085a1322a2616b2e77ac3bf3453535947. --- src/fpm_meta.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c414127a59..c70a082de6 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -93,8 +93,10 @@ module fpm_meta integer, parameter :: MPI_TYPE_INTEL = 3 integer, parameter :: MPI_TYPE_MSMPI = 4 + + !> Debugging information -logical, parameter, private :: verbose = .false. +logical, parameter, private :: verbose = .true. integer, parameter, private :: WRAPPER_FORTRAN = 1 integer, parameter, private :: WRAPPER_C = 2 @@ -770,7 +772,7 @@ subroutine get_mpi_runner(command,verbose,error) ! Try several commands do itri=1,size(try) - call find_command_location(trim(try(itri)),command%s,echo=.false.,verbose=verbose,error=error) + call find_command_location(trim(try(itri)),command%s,verbose=verbose,error=error) ! Success! success = len_trim(command%s)>0 .and. .not.allocated(error) @@ -793,7 +795,7 @@ subroutine compiler_get_path(self,path,error) type(string_t), intent(out) :: path type(error_t), allocatable, intent(out) :: error - call find_command_location(self%fc,path%s,.false.,self%verbose,error) + call find_command_location(self%fc,path%s,self%echo,self%verbose,error) end subroutine compiler_get_path From 53719bf60bf42594b843a4e5e3127d5603f3ef78 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 21 Apr 2023 11:36:22 -0500 Subject: [PATCH 371/799] fix run api --- src/fpm_meta.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c70a082de6..b70e366589 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -96,7 +96,7 @@ module fpm_meta !> Debugging information -logical, parameter, private :: verbose = .true. +logical, parameter, private :: verbose = .false. integer, parameter, private :: WRAPPER_FORTRAN = 1 integer, parameter, private :: WRAPPER_C = 2 @@ -696,9 +696,9 @@ subroutine find_command_location(command,path,echo,verbose,error) tmp_file = get_temp_filename() if (get_os_type()==OS_WINDOWS) then - call run("where "//command, echo=echo, verbose=verbose, redirect=tmp_file, exitstat=stat) + call run("where "//command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) else - call run("which "//command, echo=echo, verbose=verbose, redirect=tmp_file, exitstat=stat) + call run("which "//command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) end if if (stat/=0) then call fatal_error(error,'compiler_get_path failed for '//command) From 37f7403c02e22f209ce3adc647e6a3f0c087153a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 22 Apr 2023 02:45:44 +0700 Subject: [PATCH 372/799] Fix --show-package-version --- src/fpm/cmd/publish.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 8ca9a21d2d..561c710ae9 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -39,6 +39,10 @@ subroutine cmd_publish(settings) if (allocated(error)) call fpm_stop(1, '*cmd_build* Package error: '//error%message) version = package%version + if (settings%show_package_version) then + print *, version%s(); return + end if + ! Build model to obtain dependency tree. call build_model(model, settings%fpm_build_settings, package, error) if (allocated(error)) call fpm_stop(1, '*cmd_build* Model error: '//error%message) From a7ff907ff6945e57f0ebc43a27c236b5588fd48d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 24 Apr 2023 09:40:12 +0200 Subject: [PATCH 373/799] replace move with force-copy --- src/fpm/installer.f90 | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/fpm/installer.f90 b/src/fpm/installer.f90 index ec5880fe51..9fbe0948ae 100644 --- a/src/fpm/installer.f90 +++ b/src/fpm/installer.f90 @@ -63,6 +63,12 @@ module fpm_installer !> Copy command on Windows platforms character(len=*), parameter :: default_copy_win = "copy" + !> Copy command on Unix platforms + character(len=*), parameter :: default_force_copy_unix = "cp -f" + + !> Copy command on Windows platforms + character(len=*), parameter :: default_force_copy_win = "copy /Y" + !> Move command on Unix platforms character(len=*), parameter :: default_move_unix = "mv" @@ -93,13 +99,14 @@ subroutine new_installer(self, prefix, bindir, libdir, includedir, verbosity, & self%os = get_os_type() + ! By default, never prompt the user for overwrites if (present(copy)) then self%copy = copy else if (os_is_unix(self%os)) then - self%copy = default_copy_unix + self%copy = default_force_copy_unix else - self%copy = default_copy_win + self%copy = default_force_copy_win end if end if @@ -223,12 +230,9 @@ subroutine install(self, source, destination, error) end if end if - ! move instead of copy if already installed - if (exists(install_dest)) then - call self%run(self%move//' "'//source//'" "'//install_dest//'"', error) - else - call self%run(self%copy//' "'//source//'" "'//install_dest//'"', error) - end if + ! Use force-copy to never prompt the user for overwrite if a package was already installed + call self%run(self%copy//' "'//source//'" "'//install_dest//'"', error) + if (allocated(error)) return end subroutine install From cd4abbe4289ee2527fc3870401e9cc9d25ed2114 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 24 Apr 2023 09:40:19 +0200 Subject: [PATCH 374/799] add multiple `install`s test --- ci/run_tests.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 8e6162d81f..b0e769b73e 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -225,6 +225,14 @@ if [[ -z "$(grep Update update.log)" ]]; then exit 1; fi +# Test that no files are lost during multiple `install`s +# including overwriting the same install +"$fpm" install --prefix a +"$fpm" install --prefix a +"$fpm" install --prefix a +"$fpm" install --prefix b +"$fpm" install --prefix c + popd # Cleanup From bde53327f1b873e34078e3692e443aca9a361d17 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 24 Apr 2023 22:10:24 +0200 Subject: [PATCH 375/799] return package name on invalid build key --- src/fpm/manifest/build.f90 | 15 +++++++++++---- src/fpm/manifest/package.f90 | 6 +++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 8047dd045d..b3af26e517 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -52,7 +52,7 @@ module fpm_manifest_build !> Construct a new build configuration from a TOML data structure - subroutine new_build_config(self, table, error) + subroutine new_build_config(self, table, package_name, error) !> Instance of the build configuration type(build_config_t), intent(out) :: self @@ -60,12 +60,15 @@ subroutine new_build_config(self, table, error) !> Instance of the TOML data structure type(toml_table), intent(inout) :: table + !> Package name + character(len=*), intent(in) :: package_name + !> Error handling type(error_t), allocatable, intent(out) :: error integer :: stat - call check(table, error) + call check(table, package_name, error) if (allocated(error)) return call get_value(table, "auto-executables", self%auto_executables, .true., stat=stat) @@ -128,11 +131,14 @@ subroutine new_build_config(self, table, error) end subroutine new_build_config !> Check local schema for allowed entries - subroutine check(table, error) + subroutine check(table, package_name, error) !> Instance of the TOML data structure type(toml_table), intent(inout) :: table + !> Package name + character(len=*), intent(in) :: package_name + !> Error handling type(error_t), allocatable, intent(out) :: error @@ -154,7 +160,8 @@ subroutine check(table, error) continue case default - call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [build]") + call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [build]"//& + " building package "//package_name) exit end select diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index ddad144d75..4769101e74 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -172,7 +172,7 @@ subroutine new_package(self, table, root, error) call fatal_error(error, "Type mismatch for build entry, must be a table") return end if - call new_build_config(self%build, child, error) + call new_build_config(self%build, child, self%name, error) if (allocated(error)) return call get_value(table, "install", child, requested=.true., stat=stat) @@ -232,7 +232,7 @@ subroutine new_package(self, table, root, error) call new_library(self%library, child, error) if (allocated(error)) return end if - + call get_value(table, "profiles", child, requested=.false.) if (associated(child)) then call new_profiles(self%profiles, child, error) @@ -442,7 +442,7 @@ subroutine info(self, unit, verbosity) call self%dev_dependency(ii)%info(unit, pr - 1) end do end if - + if (allocated(self%profiles)) then if (size(self%profiles) > 1 .or. pr > 2) then write(unit, fmti) "- profiles", size(self%profiles) From 5919057b4b7d93e1003b12fddf9f4d8999fc664c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 24 Apr 2023 22:10:29 +0200 Subject: [PATCH 376/799] write test --- test/fpm_test/test_manifest.f90 | 54 +++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index ccb401b7c6..26cb310e23 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -5,6 +5,7 @@ module test_manifest use fpm_manifest use fpm_manifest_profile, only: profile_config_t, find_profile use fpm_strings, only: operator(.in.) + use fpm_error, only: fatal_error, error_t implicit none private public :: collect_manifest @@ -42,6 +43,7 @@ subroutine collect_manifest(tests) & new_unittest("build-config-valid", test_build_valid), & & new_unittest("build-config-empty", test_build_empty), & & new_unittest("build-config-invalid-values", test_build_invalid_values, should_fail=.true.), & + & new_unittest("build-key-invalid", test_build_invalid_key), & & new_unittest("library-empty", test_library_empty), & & new_unittest("library-wrongkey", test_library_wrongkey, should_fail=.true.), & & new_unittest("package-simple", test_package_simple), & @@ -693,6 +695,52 @@ subroutine test_build_valid(error) end subroutine test_build_valid + !> Try to read values from the [build] table + subroutine test_build_invalid_key(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + type(error_t), allocatable :: build_error + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & '[build]', & + & 'auto-executables = false', & + & 'auto-tests = false ', & + & 'module-naming = true ', & + & 'this-will-fail = true ' + close(unit) + + call get_package_data(package, temp_file, build_error) + + ! Error message should contain both package name and key name + if (allocated(build_error)) then + + if (.not.index(build_error%message,'this-will-fail')>0) then + call fatal_error(error, 'no invalid key name is printed to output') + return + end if + + if (.not.index(build_error%message,'example')>0) then + call fatal_error(error, 'no package name is printed to output') + return + end if + + else + call fatal_error(error, 'no error allocated on invalid [build] section key ') + return + end if + + end subroutine test_build_invalid_key + + !> Try to read values from an empty [build] table subroutine test_build_empty(error) @@ -1156,7 +1204,7 @@ subroutine test_link_string(error) table = toml_table() call set_value(table, "link", "z", stat=stat) - call new_build_config(build, table, error) + call new_build_config(build, table, 'test_link_string', error) end subroutine test_link_string @@ -1179,7 +1227,7 @@ subroutine test_link_array(error) call set_value(children, 1, "blas", stat=stat) call set_value(children, 2, "lapack", stat=stat) - call new_build_config(build, table, error) + call new_build_config(build, table, 'test_link_array', error) end subroutine test_link_array @@ -1200,7 +1248,7 @@ subroutine test_invalid_link(error) table = toml_table() call add_table(table, "link", child, stat=stat) - call new_build_config(build, table, error) + call new_build_config(build, table, 'test_invalid_link', error) end subroutine test_invalid_link From 62da310642fb88d764e5395dffd61561145f76a4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 26 Apr 2023 17:31:17 +0700 Subject: [PATCH 377/799] Fix typo --- src/fpm/manifest.f90 | 2 +- src/fpm/manifest/package.f90 | 6 ++---- src/fpm/manifest/preprocess.f90 | 4 ++-- test/fpm_test/test_manifest.f90 | 6 +++--- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 3bc2e0bf87..16c70fdbfc 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -12,7 +12,7 @@ module fpm_manifest use fpm_manifest_executable, only : executable_config_t use fpm_manifest_dependency, only : dependency_config_t use fpm_manifest_library, only : library_config_t - use fpm_mainfest_preprocess, only : preprocess_config_t + use fpm_manifest_preprocess, only : preprocess_config_t use fpm_manifest_package, only : package_config_t, new_package use fpm_error, only : error_t, fatal_error use fpm_toml, only : toml_table, read_package_file diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index ddad144d75..8551b7ae72 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -43,13 +43,11 @@ module fpm_manifest_package use fpm_manifest_library, only : library_config_t, new_library use fpm_manifest_install, only: install_config_t, new_install_config use fpm_manifest_test, only : test_config_t, new_test - use fpm_mainfest_preprocess, only : preprocess_config_t, new_preprocessors + use fpm_manifest_preprocess, only : preprocess_config_t, new_preprocessors use fpm_filesystem, only : exists, getline, join_path use fpm_error, only : error_t, fatal_error, syntax_error, bad_name_error - use fpm_toml, only : toml_table, toml_array, toml_key, toml_stat, get_value, & - & len + use fpm_toml, only : toml_table, toml_array, toml_key, toml_stat, get_value, len use fpm_versioning, only : version_t, new_version - use fpm_filesystem, only: join_path implicit none private diff --git a/src/fpm/manifest/preprocess.f90 b/src/fpm/manifest/preprocess.f90 index 6132d97210..77e31cc2bd 100644 --- a/src/fpm/manifest/preprocess.f90 +++ b/src/fpm/manifest/preprocess.f90 @@ -10,7 +10,7 @@ !> macros = [] !> ``` -module fpm_mainfest_preprocess +module fpm_manifest_preprocess use fpm_error, only : error_t, syntax_error use fpm_strings, only : string_t use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list @@ -191,4 +191,4 @@ subroutine info(self, unit, verbosity) end subroutine info -end module fpm_mainfest_preprocess +end module fpm_manifest_preprocess diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index ccb401b7c6..20283efe6d 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1270,7 +1270,7 @@ subroutine test_install_wrongkey(error) end subroutine test_install_wrongkey subroutine test_preprocess_empty(error) - use fpm_mainfest_preprocess + use fpm_manifest_preprocess use fpm_toml, only : new_table, toml_table !> Error handling @@ -1288,7 +1288,7 @@ end subroutine test_preprocess_empty !> Pass a TOML table with not allowed keys subroutine test_preprocess_wrongkey(error) - use fpm_mainfest_preprocess + use fpm_manifest_preprocess use fpm_toml, only : new_table, add_table, toml_table !> Error handling @@ -1309,7 +1309,7 @@ end subroutine test_preprocess_wrongkey !> Preprocess table cannot be empty. subroutine test_preprocessors_empty(error) - use fpm_mainfest_preprocess + use fpm_manifest_preprocess use fpm_toml, only : new_table, toml_table !> Error handling From f78f61aee8734e7818af01ca125ab4b0debabffe Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 26 Apr 2023 18:56:16 +0700 Subject: [PATCH 378/799] Add option to add macro to package --- src/fpm.f90 | 12 +++++------ src/fpm/cmd/install.f90 | 2 +- src/fpm/cmd/publish.f90 | 1 + src/fpm/cmd/update.f90 | 2 +- src/fpm/manifest.f90 | 48 ++++++++++++++++++++++++++++++++++++++++- src/fpm_os.F90 | 14 ++++++------ src/fpm_os.c | 16 -------------- 7 files changed, 61 insertions(+), 34 deletions(-) delete mode 100644 src/fpm_os.c diff --git a/src/fpm.f90 b/src/fpm.f90 index dcb2321493..0eb908e684 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -41,7 +41,7 @@ subroutine build_model(model, settings, package, error) integer :: i, j type(package_config_t) :: dependency character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags - logical :: has_cpp + logical :: has_cpp = .false. logical :: duplicates_found = .false. type(string_t) :: include_dir @@ -96,13 +96,11 @@ subroutine build_model(model, settings, package, error) allocate(model%packages(model%deps%ndep)) - has_cpp = .false. do i = 1, model%deps%ndep associate(dep => model%deps%dep(i)) manifest = join_path(dep%proj_dir, "fpm.toml") - call get_package_data(dependency, manifest, error, & - apply_defaults=.true.) + call get_package_data(dependency, manifest, error, apply_defaults=.true.) if (allocated(error)) exit model%packages(i)%name = dependency%name @@ -118,7 +116,7 @@ subroutine build_model(model, settings, package, error) if (dependency%preprocess(j)%name == "cpp") then if (.not. has_cpp) has_cpp = .true. if (allocated(dependency%preprocess(j)%macros)) then - model%packages(i)%macros = dependency%preprocess(j)%macros + model%packages(i)%macros = dependency%preprocess(j)%macros end if else write(stderr, '(a)') 'Warning: Preprocessor ' // package%preprocess(i)%name // & @@ -402,7 +400,7 @@ subroutine cmd_build(settings) integer :: i -call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) +call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) if (allocated(error)) then call fpm_stop(1,'*cmd_build* Package error: '//error%message) end if @@ -448,7 +446,7 @@ subroutine cmd_run(settings,test) character(len=:),allocatable :: line logical :: toomany - call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) + call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) if (allocated(error)) then call fpm_stop(1, '*cmd_run* Package error: '//error%message) end if diff --git a/src/fpm/cmd/install.f90 b/src/fpm/cmd/install.f90 index c260bfc4df..a78a64d169 100644 --- a/src/fpm/cmd/install.f90 +++ b/src/fpm/cmd/install.f90 @@ -31,7 +31,7 @@ subroutine cmd_install(settings) type(string_t), allocatable :: list(:) logical :: installable - call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) + call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) call handle_error(error) call build_model(model, settings%fpm_build_settings, package, error) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 561c710ae9..09fc465272 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -35,6 +35,7 @@ subroutine cmd_publish(settings) type(downloader_t) :: downloader integer :: i + ! Get package data to determine package version. call get_package_data(package, 'fpm.toml', error, apply_defaults=.true.) if (allocated(error)) call fpm_stop(1, '*cmd_build* Package error: '//error%message) version = package%version diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index e1bcb7326c..5b1b48df82 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -21,7 +21,7 @@ subroutine cmd_update(settings) integer :: ii character(len=:), allocatable :: cache - call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) + call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) call handle_error(error) if (.not.exists("build")) then diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 16c70fdbfc..e2b5c472e4 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -18,6 +18,7 @@ module fpm_manifest use fpm_toml, only : toml_table, read_package_file use fpm_manifest_test, only : test_config_t use fpm_filesystem, only: join_path, exists, dirname, is_dir + use fpm_environment, only: os_is_unix use fpm_strings, only: string_t implicit none private @@ -89,7 +90,7 @@ end subroutine default_test !> Obtain package meta data from a configuation file - subroutine get_package_data(package, file, error, apply_defaults) + subroutine get_package_data(package, file, error, apply_defaults, add_is_windows_macro) !> Parsed package meta data type(package_config_t), intent(out) :: package @@ -103,6 +104,9 @@ subroutine get_package_data(package, file, error, apply_defaults) !> Apply package defaults (uses file system operations) logical, intent(in), optional :: apply_defaults + !> Add `FPM_IS_WINDOWS` macro to the preprocessor + logical, intent(in), optional :: add_is_windows_macro + type(toml_table), allocatable :: table character(len=:), allocatable :: root @@ -126,6 +130,10 @@ subroutine get_package_data(package, file, error, apply_defaults) end if end if + if (present(add_is_windows_macro)) then + if (add_is_windows_macro) call add_fpm_is_windows_macro(package%preprocess) + end if + end subroutine get_package_data @@ -182,4 +190,42 @@ subroutine package_defaults(package, root, error) end subroutine package_defaults + !> Add the FPM_IS_WINDOWS macro if it wasn't already defined. + subroutine add_fpm_is_windows_macro(preprocessors) + !> Preprocessor configurations. + type(preprocess_config_t), allocatable, intent(inout) :: preprocessors(:) + + type(preprocess_config_t), allocatable :: new_cpp + integer :: i, j + + if (os_is_unix()) return + + if (allocated(preprocessors)) then + do i = 1, size(preprocessors) + if (preprocessors(i)%name == 'cpp') then + if (allocated(preprocessors(i)%macros)) then + ! Return if macro is already defined. + do j = 1, size(preprocessors(i)%macros) + if (preprocessors(i)%macros(i)%s == 'FPM_IS_WINDOWS') return + end do + ! Macro not found, therefore add it. + allocate(preprocessors(i)%macros(size(preprocessors(i)%macros) + 1)) + else + allocate(preprocessors(i)%macros(1)) + end if + preprocessors(i)%macros(size(preprocessors(i)%macros))%s = 'FPM_IS_WINDOWS' + return + end if + end do + end if + + ! Add cpp macro if it was not already defined. + if (.not. allocated(preprocessors)) allocate(preprocessors(1)) + new_cpp%name = 'cpp' + allocate(new_cpp%macros(1)) + new_cpp%macros(1)%s = 'FPM_IS_WINDOWS' + preprocessors(1) = new_cpp + end + + end module fpm_manifest diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 1acd9653ae..e3a7fd166c 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -47,16 +47,14 @@ function realpath(path, resolved_path) result(ptr) bind(C) type(c_ptr) :: ptr end function realpath - !> Determine the absolute, canonicalized path for a given path. - !> Calls custom C routine and is able to distinguish between Unix and Windows. - function c_realpath(path, resolved_path, maxLength) result(ptr) & - bind(C, name="c_realpath") + !> Determine the absolute, canonicalized path for a given path. Windows-only. + function fullpath(resolved_path, path, maxLength) result(ptr) bind(C, name="_fullpath") import :: c_ptr, c_char, c_int character(kind=c_char, len=1), intent(in) :: path(*) character(kind=c_char, len=1), intent(out) :: resolved_path(*) integer(c_int), value, intent(in) :: maxLength type(c_ptr) :: ptr - end function c_realpath + end function fullpath end interface contains @@ -145,11 +143,11 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) -! The _WIN32 macro is currently not exported using gfortran. -#if defined(FPM_BOOTSTRAP) && !defined(_WIN32) +! The _WIN32 macro is currently not exported using gfortran. Therefore using FPM_IS_WINDOWS. +#ifndef FPM_IS_WINDOWS ptr = realpath(appended_path, cpath) #else - ptr = c_realpath(appended_path, cpath, buffersize) + ptr = fullpath(cpath, appended_path, buffersize) #endif if (c_associated(ptr)) then diff --git a/src/fpm_os.c b/src/fpm_os.c deleted file mode 100644 index 2d417a0695..0000000000 --- a/src/fpm_os.c +++ /dev/null @@ -1,16 +0,0 @@ -#include - -/// @brief Determine the absolute, canonicalized path for a given path. -/// @param path -/// @param resolved_path -/// @param maxLength -/// @return -int c_realpath(char* path, char* resolved_path, int maxLength) { -// Checking macro in C because it doesn't work with gfortran on Windows, even -// when exported manually. -#ifndef _WIN32 - return realpath(path, resolved_path); -#else - return _fullpath(resolved_path, path, maxLength); -#endif -} From a06dee50062baad8f11c7140a49d8ab013441a0b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 26 Apr 2023 19:07:27 +0700 Subject: [PATCH 379/799] Make testable --- src/fpm/manifest.f90 | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index e2b5c472e4..158197c002 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -191,14 +191,24 @@ end subroutine package_defaults !> Add the FPM_IS_WINDOWS macro if it wasn't already defined. - subroutine add_fpm_is_windows_macro(preprocessors) + subroutine add_fpm_is_windows_macro(preprocessors, is_unix) !> Preprocessor configurations. type(preprocess_config_t), allocatable, intent(inout) :: preprocessors(:) + !> Whether the operating system is Unix-like. + logical, intent(in), optional :: is_unix + type(preprocess_config_t), allocatable :: new_cpp integer :: i, j + logical :: is_unix_os = .true. + + if (present(is_unix)) then + is_unix_os = is_unix + else + is_unix_os = os_is_unix() + end if - if (os_is_unix()) return + if (is_unix_os) return if (allocated(preprocessors)) then do i = 1, size(preprocessors) From c8d476a70a9dc35859011f690276aeda8fbf04bb Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 26 Apr 2023 22:20:56 +0700 Subject: [PATCH 380/799] Fix allocation --- src/fpm/manifest.f90 | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 158197c002..394e69dd3e 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -219,22 +219,23 @@ subroutine add_fpm_is_windows_macro(preprocessors, is_unix) if (preprocessors(i)%macros(i)%s == 'FPM_IS_WINDOWS') return end do ! Macro not found, therefore add it. - allocate(preprocessors(i)%macros(size(preprocessors(i)%macros) + 1)) + preprocessors(i)%macros = [preprocessors(i)%macros, string_t('FPM_IS_WINDOWS')] else - allocate(preprocessors(i)%macros(1)) + preprocessors(i)%macros = [string_t('FPM_IS_WINDOWS')] end if - preprocessors(i)%macros(size(preprocessors(i)%macros))%s = 'FPM_IS_WINDOWS' return end if end do end if - ! Add cpp macro if it was not already defined. - if (.not. allocated(preprocessors)) allocate(preprocessors(1)) + ! No cpp macros found, add one. new_cpp%name = 'cpp' - allocate(new_cpp%macros(1)) - new_cpp%macros(1)%s = 'FPM_IS_WINDOWS' - preprocessors(1) = new_cpp + new_cpp%macros = [string_t('FPM_IS_WINDOWS')] + if (allocated(preprocessors)) then + preprocessors = [preprocessors, new_cpp] + else + preprocessors = [new_cpp] + end if end From d97d240583c976c4507cccc06910a6f5e051fd84 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 27 Apr 2023 11:28:45 +0700 Subject: [PATCH 381/799] Remove allocatable attribute --- src/fpm/manifest.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 394e69dd3e..68f5a885c3 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -198,7 +198,7 @@ subroutine add_fpm_is_windows_macro(preprocessors, is_unix) !> Whether the operating system is Unix-like. logical, intent(in), optional :: is_unix - type(preprocess_config_t), allocatable :: new_cpp + type(preprocess_config_t) :: new_cpp integer :: i, j logical :: is_unix_os = .true. From c483944bb4b9902934e2e9353d78ab3c8e3965ee Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 27 Apr 2023 12:31:46 +0700 Subject: [PATCH 382/799] Fix index --- src/fpm/manifest.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 68f5a885c3..d2e604ca4f 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -216,7 +216,7 @@ subroutine add_fpm_is_windows_macro(preprocessors, is_unix) if (allocated(preprocessors(i)%macros)) then ! Return if macro is already defined. do j = 1, size(preprocessors(i)%macros) - if (preprocessors(i)%macros(i)%s == 'FPM_IS_WINDOWS') return + if (preprocessors(i)%macros(j)%s == 'FPM_IS_WINDOWS') return end do ! Macro not found, therefore add it. preprocessors(i)%macros = [preprocessors(i)%macros, string_t('FPM_IS_WINDOWS')] From e2bd9bad8fc4e293aaca88d724b9417e5e00dc41 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 27 Apr 2023 12:55:33 +0700 Subject: [PATCH 383/799] Export macro for all dependencies --- src/fpm.f90 | 4 ++-- src/fpm/cmd/install.f90 | 2 +- src/fpm/cmd/update.f90 | 2 +- src/fpm/manifest.f90 | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 0eb908e684..c70b420a46 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -400,7 +400,7 @@ subroutine cmd_build(settings) integer :: i -call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) +call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) if (allocated(error)) then call fpm_stop(1,'*cmd_build* Package error: '//error%message) end if @@ -446,7 +446,7 @@ subroutine cmd_run(settings,test) character(len=:),allocatable :: line logical :: toomany - call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) + call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) if (allocated(error)) then call fpm_stop(1, '*cmd_run* Package error: '//error%message) end if diff --git a/src/fpm/cmd/install.f90 b/src/fpm/cmd/install.f90 index a78a64d169..c260bfc4df 100644 --- a/src/fpm/cmd/install.f90 +++ b/src/fpm/cmd/install.f90 @@ -31,7 +31,7 @@ subroutine cmd_install(settings) type(string_t), allocatable :: list(:) logical :: installable - call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) + call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) call handle_error(error) call build_model(model, settings%fpm_build_settings, package, error) diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index 5b1b48df82..e1bcb7326c 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -21,7 +21,7 @@ subroutine cmd_update(settings) integer :: ii character(len=:), allocatable :: cache - call get_package_data(package, "fpm.toml", error, apply_defaults=.true., add_is_windows_macro=.true.) + call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) call handle_error(error) if (.not.exists("build")) then diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index d2e604ca4f..3b3d42d79d 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -109,11 +109,12 @@ subroutine get_package_data(package, file, error, apply_defaults, add_is_windows type(toml_table), allocatable :: table character(len=:), allocatable :: root + logical :: set_is_windows_macro = .true. call read_package_file(table, file, error) if (allocated(error)) return - if (.not.allocated(table)) then + if (.not. allocated(table)) then call fatal_error(error, "Unclassified error while reading: '"//file//"'") return end if @@ -130,9 +131,8 @@ subroutine get_package_data(package, file, error, apply_defaults, add_is_windows end if end if - if (present(add_is_windows_macro)) then - if (add_is_windows_macro) call add_fpm_is_windows_macro(package%preprocess) - end if + if (present(add_is_windows_macro)) set_is_windows_macro = add_is_windows_macro + if (set_is_windows_macro) call add_fpm_is_windows_macro(package%preprocess) end subroutine get_package_data From b7c71e3642ee840b04221ddb9c28dba5d5c23955 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 02:14:57 -0500 Subject: [PATCH 384/799] add ubuntu --- .github/workflows/meta.yml | 44 ++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index a7561abdbc..9e99df547f 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -6,6 +6,8 @@ on: - 'src/*meta*.f90' # On push, only launch job if something has changed in the metapackages - 'src/fpm/*meta*.f90' - 'src/fpm/manifest/*meta*.f90' + - 'src/ci/meta_tests.sh' + - 'src/.github/workflows/meta.yml' pull_request: release: types: [published] @@ -25,12 +27,18 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11,windows-latest] - mpi: [mpich,openmpi,msmpi] - gcc_v: [10] # Version of GFortran we want to use + os: [macos-11,windows-latest,ubuntu-latest] + mpi: [mpich,openmpi,msmpi] + gcc_v: [10] # Version of GFortran we want to use exclude: - os: macos-11 mpi: msmpi + - os: macos-11 # temporary + mpi: openmpi + - os: macos-11 # temporary + mpi: mpich + - os: windows-latest # temporary + mpi: msmpi - os: windows-latest mpi: mpich - os: windows-latest @@ -55,7 +63,7 @@ jobs: - uses: msys2/setup-msys2@v2 if: contains(matrix.os,'windows') with: - msystem: MINGW64 + msystem: MINGW64 update: true install: >- git @@ -65,52 +73,52 @@ jobs: curl gcc-fortran - - name: Put MSYS2_MinGW64 on PATH + - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: download MS-MPI setup (SDK is from MSYS2) + - name: (Windows) download MS-MPI setup (SDK is from MSYS2) if: contains(matrix.os,'windows') run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.1/msmpisetup.exe - - name: Install mpiexec.exe (-force needed to bypass GUI on headless) + - name: (Windows) Install mpiexec.exe (-force needed to bypass GUI on headless) if: contains(matrix.os,'windows') run: .\msmpisetup.exe -unattend -force - - name: test that mpiexec.exe exists + - name: (Windows) test that mpiexec.exe exists if: contains(matrix.os,'windows') # can't use MSMPI_BIN as Actions doesn't update PATH from msmpisetup.exe run: Test-Path "C:\Program Files\Microsoft MPI\Bin\mpiexec.exe" -PathType leaf - - name: put MSMPI_BIN on PATH (where mpiexec is) + - name: (Windows) put MSMPI_BIN on PATH (where mpiexec is) if: contains(matrix.os,'windows') run: echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Install MSYS2 msmpi package + - name: (Windows) Install MSYS2 msmpi package if: contains(matrix.os,'windows') shell: msys2 {0} run: pacman --noconfirm -S mingw-w64-x86_64-msmpi - - name: Set up Homebrew + - name: (macOS) Set up Homebrew if: contains(matrix.os,'macos') id: set-up-homebrew - uses: Homebrew/actions/setup-homebrew@master + uses: Homebrew/actions/setup-homebrew@master - - name: Install Homebrew gfortran + - name: (macOS) Install Homebrew gfortran if: contains(matrix.os, 'macos') run: | brew install gcc@${GCC_V} ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran which gfortran-${GCC_V} which gfortran - - - name: Install homebrew MPICH + + - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') run: | brew install mpich - - name: Install homebrew OpenMPI + - name: (macOS) Install homebrew OpenMPI if: contains(matrix.mpi,'openmpi') && contains(matrix.os,'macos') run: | brew install --cc=gcc-${GCC_V} openmpi @@ -130,7 +138,7 @@ jobs: - name: Build Fortran fpm (bootstrap) shell: bash run: | - ${{ env.BOOTSTRAP }} build + ${{ env.BOOTSTRAP }} build - name: Run Fortran fpm (bootstrap) shell: bash @@ -201,7 +209,7 @@ jobs: env: EXE: fpm-${{ env.VERSION }}-${{ matrix.os-arch }}${{ matrix.exe }} - - name: Run metapackage tests using the release version + - name: Run metapackage tests using the release version shell: bash run: | ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From 5b6aaa4126962396845bfc47f19c417d814c6eff Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 03:51:59 -0500 Subject: [PATCH 385/799] replace `unix` with `is_unix` to avoid intel fpp collisions --- src/fpm.f90 | 12 ++++++------ src/fpm/dependency.f90 | 6 +++--- src/fpm_command_line.f90 | 8 ++++---- src/fpm_environment.f90 | 4 ++-- src/fpm_filesystem.F90 | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index dcb2321493..224e49d409 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -641,9 +641,9 @@ end subroutine compact_list end subroutine cmd_run -subroutine delete_skip(unix) +subroutine delete_skip(is_unix) !> delete directories in the build folder, skipping dependencies - logical, intent(in) :: unix + logical, intent(in) :: is_unix character(len=:), allocatable :: dir type(string_t), allocatable :: files(:) integer :: i @@ -651,7 +651,7 @@ subroutine delete_skip(unix) do i = 1, size(files) if (is_dir(files(i)%s)) then dir = files(i)%s - if (.not.str_ends_with(dir,'dependencies')) call os_delete_dir(unix, dir) + if (.not.str_ends_with(dir,'dependencies')) call os_delete_dir(is_unix, dir) end if end do end subroutine delete_skip @@ -665,18 +665,18 @@ subroutine cmd_clean(settings) if (is_dir('build')) then ! remove the entire build directory if (settings%clean_call) then - call os_delete_dir(settings%unix, 'build') + call os_delete_dir(settings%is_unix, 'build') return end if ! remove the build directory but skip dependencies if (settings%clean_skip) then - call delete_skip(settings%unix) + call delete_skip(settings%is_unix) return end if ! prompt to remove the build directory but skip dependencies write(stdout, '(A)', advance='no') "Delete build, excluding dependencies (y/n)? " read(stdin, '(A1)') response - if (lower(response) == 'y') call delete_skip(settings%unix) + if (lower(response) == 'y') call delete_skip(settings%is_unix) else write (stdout, '(A)') "fpm: No build directory found." end if diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index a12078f5e4..c571e41a8d 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1013,7 +1013,7 @@ subroutine load_from_toml(self, table, error) type(error_t), allocatable, intent(out) :: error integer :: ndep, ii - logical :: unix + logical :: is_unix character(len=:), allocatable :: version, url, obj, rev, proj_dir type(toml_key), allocatable :: list(:) type(toml_table), pointer :: ptr @@ -1025,7 +1025,7 @@ subroutine load_from_toml(self, table, error) call resize(self%dep, ndep + ndep/2 + size(list)) end if - unix = get_os_type() /= OS_WINDOWS + is_unix = get_os_type() /= OS_WINDOWS do ii = 1, size(list) call get_value(table, list(ii)%key, ptr) @@ -1038,7 +1038,7 @@ subroutine load_from_toml(self, table, error) self%ndep = self%ndep + 1 associate (dep => self%dep(self%ndep)) dep%name = list(ii)%key - if (unix) then + if (is_unix) then dep%proj_dir = proj_dir else dep%proj_dir = windows_path(proj_dir) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 0a68e501b1..2601b5c63f 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -112,7 +112,7 @@ module fpm_command_line end type type, extends(fpm_cmd_settings) :: fpm_clean_settings - logical :: unix + logical :: is_unix character(len=:), allocatable :: calling_dir ! directory clean called from logical :: clean_skip=.false. logical :: clean_call=.false. @@ -216,7 +216,7 @@ subroutine get_command_line_settings(cmd_settings) character(len=4096) :: cmdarg integer :: i integer :: os - logical :: unix + logical :: is_unix type(fpm_install_settings), allocatable :: install_settings type(fpm_publish_settings), allocatable :: publish_settings type(version_t) :: version @@ -243,7 +243,7 @@ subroutine get_command_line_settings(cmd_settings) case (OS_UNKNOWN); os_type = "OS Type: Unknown" case default ; os_type = "OS Type: UNKNOWN" end select - unix = os_is_unix(os) + is_unix = os_is_unix(os) ! Get current release version version = fpm_version() @@ -613,7 +613,7 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_clean_settings :: cmd_settings) call get_current_directory(working_dir, error) cmd_settings=fpm_clean_settings( & - & unix=unix, & + & is_unix=is_unix, & & calling_dir=working_dir, & & clean_skip=lget('skip'), & clean_call=lget('all')) diff --git a/src/fpm_environment.f90 b/src/fpm_environment.f90 index c8bd9be5d5..7e8aa2317d 100644 --- a/src/fpm_environment.f90 +++ b/src/fpm_environment.f90 @@ -145,7 +145,7 @@ end function get_os_type !> Compare the output of [[get_os_type]] or the optional !! passed INTEGER value to the value for OS_WINDOWS !! and return .TRUE. if they match and .FALSE. otherwise - logical function os_is_unix(os) result(unix) + logical function os_is_unix(os) integer, intent(in), optional :: os integer :: build_os if (present(os)) then @@ -153,7 +153,7 @@ logical function os_is_unix(os) result(unix) else build_os = get_os_type() end if - unix = build_os /= OS_WINDOWS + os_is_unix = build_os /= OS_WINDOWS end function os_is_unix !> get named environment variable value. It it is blank or diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index b192107afc..c7b12a8b5e 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -928,12 +928,12 @@ subroutine run(cmd,echo,exitstat,verbose,redirect) end subroutine run !> Delete directory using system OS remove directory commands -subroutine os_delete_dir(unix, dir, echo) - logical, intent(in) :: unix +subroutine os_delete_dir(is_unix, dir, echo) + logical, intent(in) :: is_unix character(len=*), intent(in) :: dir logical, intent(in), optional :: echo - if (unix) then + if (is_unix) then call run('rm -rf ' // dir, echo=echo,verbose=.false.) else call run('rmdir /s/q ' // dir, echo=echo,verbose=.false.) From 3632e1da4b02893017d864888debbdef1458b196 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 04:11:39 -0500 Subject: [PATCH 386/799] unix -fpp macro fix --- src/fpm.f90 | 12 ++++++------ src/fpm/dependency.f90 | 6 +++--- src/fpm_command_line.f90 | 8 ++++---- src/fpm_environment.f90 | 4 ++-- src/fpm_filesystem.F90 | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 07666fb2f9..c8ff464d79 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -663,9 +663,9 @@ end subroutine compact_list end subroutine cmd_run -subroutine delete_skip(unix) +subroutine delete_skip(is_unix) !> delete directories in the build folder, skipping dependencies - logical, intent(in) :: unix + logical, intent(in) :: is_unix character(len=:), allocatable :: dir type(string_t), allocatable :: files(:) integer :: i @@ -673,7 +673,7 @@ subroutine delete_skip(unix) do i = 1, size(files) if (is_dir(files(i)%s)) then dir = files(i)%s - if (.not.str_ends_with(dir,'dependencies')) call os_delete_dir(unix, dir) + if (.not.str_ends_with(dir,'dependencies')) call os_delete_dir(is_unix, dir) end if end do end subroutine delete_skip @@ -687,18 +687,18 @@ subroutine cmd_clean(settings) if (is_dir('build')) then ! remove the entire build directory if (settings%clean_call) then - call os_delete_dir(settings%unix, 'build') + call os_delete_dir(settings%is_unix, 'build') return end if ! remove the build directory but skip dependencies if (settings%clean_skip) then - call delete_skip(settings%unix) + call delete_skip(settings%is_unix) return end if ! prompt to remove the build directory but skip dependencies write(stdout, '(A)', advance='no') "Delete build, excluding dependencies (y/n)? " read(stdin, '(A1)') response - if (lower(response) == 'y') call delete_skip(settings%unix) + if (lower(response) == 'y') call delete_skip(settings%is_unix) else write (stdout, '(A)') "fpm: No build directory found." end if diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index d89b6eb836..e8804006ee 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1014,7 +1014,7 @@ subroutine load_from_toml(self, table, error) type(error_t), allocatable, intent(out) :: error integer :: ndep, ii - logical :: unix + logical :: is_unix character(len=:), allocatable :: version, url, obj, rev, proj_dir type(toml_key), allocatable :: list(:) type(toml_table), pointer :: ptr @@ -1026,7 +1026,7 @@ subroutine load_from_toml(self, table, error) call resize(self%dep, ndep + ndep/2 + size(list)) end if - unix = get_os_type() /= OS_WINDOWS + is_unix = get_os_type() /= OS_WINDOWS do ii = 1, size(list) call get_value(table, list(ii)%key, ptr) @@ -1039,7 +1039,7 @@ subroutine load_from_toml(self, table, error) self%ndep = self%ndep + 1 associate (dep => self%dep(self%ndep)) dep%name = list(ii)%key - if (unix) then + if (is_unix) then dep%proj_dir = proj_dir else dep%proj_dir = windows_path(proj_dir) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 867eecb76a..1f52f42558 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -111,7 +111,7 @@ module fpm_command_line end type type, extends(fpm_cmd_settings) :: fpm_clean_settings - logical :: unix + logical :: is_unix character(len=:), allocatable :: calling_dir ! directory clean called from logical :: clean_skip=.false. logical :: clean_call=.false. @@ -209,7 +209,7 @@ subroutine get_command_line_settings(cmd_settings) character(len=4096) :: cmdarg integer :: i integer :: os - logical :: unix + logical :: is_unix type(fpm_install_settings), allocatable :: install_settings type(version_t) :: version character(len=:), allocatable :: common_args, compiler_args, run_args, working_dir, & @@ -235,7 +235,7 @@ subroutine get_command_line_settings(cmd_settings) case (OS_UNKNOWN); os_type = "OS Type: Unknown" case default ; os_type = "OS Type: UNKNOWN" end select - unix = os_is_unix(os) + is_unix = os_is_unix(os) ! Get current release version version = fpm_version() @@ -603,7 +603,7 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_clean_settings :: cmd_settings) call get_current_directory(working_dir, error) cmd_settings=fpm_clean_settings( & - & unix=unix, & + & is_unix=is_unix, & & calling_dir=working_dir, & & clean_skip=lget('skip'), & clean_call=lget('all')) diff --git a/src/fpm_environment.f90 b/src/fpm_environment.f90 index c8bd9be5d5..7e8aa2317d 100644 --- a/src/fpm_environment.f90 +++ b/src/fpm_environment.f90 @@ -145,7 +145,7 @@ end function get_os_type !> Compare the output of [[get_os_type]] or the optional !! passed INTEGER value to the value for OS_WINDOWS !! and return .TRUE. if they match and .FALSE. otherwise - logical function os_is_unix(os) result(unix) + logical function os_is_unix(os) integer, intent(in), optional :: os integer :: build_os if (present(os)) then @@ -153,7 +153,7 @@ logical function os_is_unix(os) result(unix) else build_os = get_os_type() end if - unix = build_os /= OS_WINDOWS + os_is_unix = build_os /= OS_WINDOWS end function os_is_unix !> get named environment variable value. It it is blank or diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 3846654354..aa771ab8c3 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -927,12 +927,12 @@ subroutine run(cmd,echo,exitstat,verbose,redirect) end subroutine run !> Delete directory using system OS remove directory commands -subroutine os_delete_dir(unix, dir, echo) - logical, intent(in) :: unix +subroutine os_delete_dir(is_unix, dir, echo) + logical, intent(in) :: is_unix character(len=*), intent(in) :: dir logical, intent(in), optional :: echo - if (unix) then + if (is_unix) then call run('rm -rf ' // dir, echo=echo,verbose=.false.) else call run('rmdir /s/q ' // dir, echo=echo,verbose=.false.) From 831f284b736e7f29675e0bfabaa0074ccfb4c628 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 05:50:19 -0500 Subject: [PATCH 387/799] base ifort implementation --- src/fpm_compiler.F90 | 12 +- src/fpm_meta.f90 | 282 +++++++++++++++++++++++++++++++------------ 2 files changed, 215 insertions(+), 79 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 374a3ad3ce..54b146a4ef 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -105,6 +105,8 @@ module fpm_compiler procedure :: link !> Check whether compiler is recognized procedure :: is_unknown + !> Check whether this is an Intel compiler + procedure :: is_intel !> Enumerate libraries, based on compiler and platform procedure :: enumerate_libraries !> Return compiler name @@ -203,7 +205,7 @@ module fpm_compiler flag_nag_openmp = " -openmp", & flag_nag_free_form = " -free", & flag_nag_fixed_form = " -fixed", & - flag_nag_no_implicit_typing = " -u" + flag_nag_no_implicit_typing = " -u" character(*), parameter :: & flag_lfortran_opt = " --fast", & @@ -217,7 +219,7 @@ module fpm_compiler flag_cray_implicit_typing = " -el", & flag_cray_fixed_form = " -ffixed", & flag_cray_free_form = " -ffree" - + contains @@ -891,6 +893,12 @@ pure function is_unknown(self) is_unknown = self%id == id_unknown end function is_unknown +pure logical function is_intel(self) + class(compiler_t), intent(in) :: self + is_intel = any(self%id == [id_intel_classic_nix,id_intel_classic_mac,id_intel_classic_windows, & + id_intel_llvm_nix,id_intel_llvm_windows,id_intel_llvm_unknown]) +end function is_intel + !> !> Enumerate libraries, based on compiler and platform !> diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index b70e366589..f246486dd4 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -92,18 +92,31 @@ module fpm_meta integer, parameter :: MPI_TYPE_MPICH = 2 integer, parameter :: MPI_TYPE_INTEL = 3 integer, parameter :: MPI_TYPE_MSMPI = 4 - - +public :: MPI_TYPE_NAME !> Debugging information logical, parameter, private :: verbose = .false. -integer, parameter, private :: WRAPPER_FORTRAN = 1 -integer, parameter, private :: WRAPPER_C = 2 -integer, parameter, private :: WRAPPER_CXX = 3 +integer, parameter, private :: LANG_FORTRAN = 1 +integer, parameter, private :: LANG_C = 2 +integer, parameter, private :: LANG_CXX = 3 contains +!> Return a name for the MPI library +pure function MPI_TYPE_NAME(mpilib) result(name) + integer, intent(in) :: mpilib + character(len=:), allocatable :: name + select case (mpilib) + case (MPI_TYPE_NONE); name = "none" + case (MPI_TYPE_OPENMPI); name = "OpenMPI" + case (MPI_TYPE_MPICH); name = "MPICH" + case (MPI_TYPE_INTEL); name = "INTELMPI" + case (MPI_TYPE_MSMPI); name = "MS-MPI" + case default; name = "UNKNOWN" + end select +end function MPI_TYPE_NAME + !> Clean the metapackage structure elemental subroutine destroy(this) class(metapackage_t), intent(inout) :: this @@ -420,18 +433,25 @@ subroutine init_mpi(this,compiler,error) type(string_t) :: output,fwrap,cwrap,cxxwrap character(256) :: msg_out character(len=:), allocatable :: tokens(:) - integer :: wcfit(3),ic,icpp,i + integer :: wcfit(3),mpilib(3),ic,icpp,i logical :: found !> Cleanup call destroy(this) + print *, 'init wrappers' + !> Get all candidate MPI wrappers call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) if (verbose) print 1, size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) - wcfit = wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) + print *, 'wrapper compiler fit' + + call wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,wcfit,mpilib,error) + + print *, 'wcfit = ',wcfit + print *, 'mpilib = ',mpilib if (allocated(error) .or. all(wcfit==0)) then @@ -447,21 +467,23 @@ subroutine init_mpi(this,compiler,error) else - if (wcfit(WRAPPER_FORTRAN)>0) fwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) - if (wcfit(WRAPPER_C)>0) cwrap = c_wrappers (wcfit(WRAPPER_C)) - if (wcfit(WRAPPER_CXX)>0) cxxwrap = cpp_wrappers (wcfit(WRAPPER_CXX)) + if (wcfit(LANG_FORTRAN)>0) fwrap = fort_wrappers(wcfit(LANG_FORTRAN)) + if (wcfit(LANG_C)>0) cwrap = c_wrappers (wcfit(LANG_C)) + if (wcfit(LANG_CXX)>0) cxxwrap = cpp_wrappers (wcfit(LANG_CXX)) + + print *, 'wcfit' !> If there's only an available Fortran wrapper, and the compiler's different than fpm's baseline !> fortran compiler suite, we still want to enable C language flags as that is most likely being !> ABI-compatible anyways. However, issues may arise. !> see e.g. Homebrew with clabng C/C++ and GNU fortran at https://gitlab.kitware.com/cmake/cmake/-/issues/18139 - if (wcfit(WRAPPER_FORTRAN)>0 .and. wcfit(WRAPPER_C)==0 .and. wcfit(WRAPPER_CXX)==0) then - cwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) - cxxwrap = fort_wrappers(wcfit(WRAPPER_FORTRAN)) + if (wcfit(LANG_FORTRAN)>0 .and. wcfit(LANG_C)==0 .and. wcfit(LANG_CXX)==0) then + cwrap = fort_wrappers(wcfit(LANG_FORTRAN)) + cxxwrap = fort_wrappers(wcfit(LANG_FORTRAN)) end if !> Initialize MPI package from wrapper command - call init_mpi_from_wrappers(this,compiler,fwrap,cwrap,cxxwrap,error) + call init_mpi_from_wrappers(this,compiler,mpilib(LANG_FORTRAN),fwrap,cwrap,cxxwrap,error) if (allocated(error)) return end if @@ -479,17 +501,17 @@ logical function is_64bit_environment() end function is_64bit_environment !> Check if there is a wrapper-compiler fit -function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,error) result(wrap) +subroutine wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,wrap,mpi,error) type(string_t), allocatable, intent(in) :: fort_wrappers(:),c_wrappers(:),cpp_wrappers(:) type(compiler_t), intent(in) :: compiler type(error_t), allocatable, intent(out) :: error - integer :: wrap(3) + integer, intent(out), dimension(3) :: wrap, mpi logical :: has_wrappers - integer :: mpif90,mpic,mpicxx type(error_t), allocatable :: wrap_error wrap = 0 + mpi = MPI_TYPE_NONE !> Were any wrappers found? has_wrappers = size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)>0 @@ -497,9 +519,9 @@ function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,err if (has_wrappers) then !> Find a Fortran wrapper for the current compiler - wrap(WRAPPER_FORTRAN) = mpi_compiler_match(fort_wrappers,compiler,wrap_error) - wrap(WRAPPER_C ) = mpi_compiler_match(c_wrappers,compiler,wrap_error) - wrap(WRAPPER_CXX ) = mpi_compiler_match(cpp_wrappers,compiler,wrap_error) + call mpi_compiler_match(LANG_FORTRAN,fort_wrappers,compiler,wrap(LANG_FORTRAN),mpi(LANG_FORTRAN),wrap_error) + call mpi_compiler_match(LANG_C, c_wrappers,compiler,wrap(LANG_C),mpi(LANG_C),wrap_error) + call mpi_compiler_match(LANG_CXX, cpp_wrappers,compiler,wrap(LANG_CXX),mpi(LANG_CXX),wrap_error) if (all(wrap==0)) then call fatal_error(error,'no valid wrappers match current compiler, '//compiler_name(compiler)) @@ -508,7 +530,7 @@ function wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,err endif -end function wrapper_compiler_fit +end subroutine wrapper_compiler_fit !> Check if a local MS-MPI SDK build is found logical function msmpi_init(this,compiler,error) result(found) @@ -926,9 +948,10 @@ function get_dos_path(path,error) end function get_dos_path !> Initialize an MPI metapackage from a valid wrapper command ('mpif90', etc...) -subroutine init_mpi_from_wrappers(this,compiler,fort_wrapper,c_wrapper,cxx_wrapper,error) +subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cxx_wrapper,error) class(metapackage_t), intent(inout) :: this type(compiler_t), intent(in) :: compiler + integer, intent(in) :: mpilib type(string_t), intent(in) :: fort_wrapper,c_wrapper,cxx_wrapper type(error_t), allocatable, intent(out) :: error @@ -938,23 +961,25 @@ subroutine init_mpi_from_wrappers(this,compiler,fort_wrapper,c_wrapper,cxx_wrapp call destroy(this) ! Get linking flags - this%link_flags = mpi_wrapper_query(fort_wrapper,'link',verbose,error) - if (allocated(error)) return - this%has_link_flags = len_trim(this%link_flags)>0 + if (mpilib/=MPI_TYPE_INTEL) then + this%link_flags = mpi_wrapper_query(mpilib,fort_wrapper,'link',verbose,error) + if (allocated(error)) return + this%has_link_flags = len_trim(this%link_flags)>0 + endif ! Add heading space - this%link_flags = string_t(' '//this%link_flags%s) + if (this%has_link_flags) this%link_flags = string_t(' '//this%link_flags%s) ! Add language-specific flags - call set_language_flags(fort_wrapper,this%has_fortran_flags,this%fflags,verbose,error) + call set_language_flags(mpilib,fort_wrapper,this%has_fortran_flags,this%fflags,verbose,error) if (allocated(error)) return - call set_language_flags(c_wrapper,this%has_c_flags,this%cflags,verbose,error) + call set_language_flags(mpilib,c_wrapper,this%has_c_flags,this%cflags,verbose,error) if (allocated(error)) return - call set_language_flags(cxx_wrapper,this%has_cxx_flags,this%cxxflags,verbose,error) + call set_language_flags(mpilib,cxx_wrapper,this%has_cxx_flags,this%cxxflags,verbose,error) if (allocated(error)) return ! Get library version - version = mpi_version_get(fort_wrapper,error) + version = mpi_version_get(mpilib,fort_wrapper,error) if (allocated(error)) then return else @@ -962,13 +987,14 @@ subroutine init_mpi_from_wrappers(this,compiler,fort_wrapper,c_wrapper,cxx_wrapp end if !> Add default run command, if present - this%run_command = mpi_wrapper_query(fort_wrapper,'runner',verbose,error) + this%run_command = mpi_wrapper_query(mpilib,fort_wrapper,'runner',verbose,error) if (allocated(error)) return this%has_run_command = len_trim(this%run_command)>0 contains - subroutine set_language_flags(wrapper,has_flags,flags,verbose,error) + subroutine set_language_flags(mpilib,wrapper,has_flags,flags,verbose,error) + integer, intent(in) :: mpilib type(string_t), intent(in) :: wrapper logical, intent(inout) :: has_flags type(string_t), intent(inout) :: flags @@ -977,7 +1003,7 @@ subroutine set_language_flags(wrapper,has_flags,flags,verbose,error) ! Get build flags for each language if (len_trim(wrapper)>0) then - flags = mpi_wrapper_query(wrapper,'flags',verbose,error) + flags = mpi_wrapper_query(mpilib,wrapper,'flags',verbose,error) if (allocated(error)) return has_flags = len_trim(flags)>0 @@ -994,9 +1020,11 @@ end subroutine set_language_flags end subroutine init_mpi_from_wrappers !> Match one of the available compiler wrappers with the current compiler -integer function mpi_compiler_match(wrappers,compiler,error) +subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) + integer, intent(in) :: language type(string_t), intent(in) :: wrappers(:) type(compiler_t), intent(in) :: compiler + integer, intent(out) :: which_one, mpilib type(error_t), allocatable, intent(out) :: error integer :: i @@ -1004,23 +1032,44 @@ integer function mpi_compiler_match(wrappers,compiler,error) character(128) :: msg_out type(compiler_t) :: mpi_compiler - mpi_compiler_match = 0 + which_one = 0 + mpilib = MPI_TYPE_NONE do i=1,size(wrappers) - screen = mpi_wrapper_query(wrappers(i),'compiler',verbose=.false.,error=error) - if (allocated(error)) return - - ! Build compiler type - call new_compiler(mpi_compiler,screen%s,'','',echo=.true.,verbose=.true.) + print *, 'TEST WRAPPER '//wrappers(i)%s - ! Match found! - if (mpi_compiler%id == compiler%id) then + mpilib = which_mpi_library(wrappers(i),compiler,verbose=.false.) - mpi_compiler_match = i - return + screen = mpi_wrapper_query(mpilib,wrappers(i),'compiler',verbose=.false.,error=error) + if (allocated(error)) return - end if + print *, 'screen <'//screen%s//'> compiler ',compiler%fc + + + select case (language) + case (LANG_FORTRAN) + ! Build compiler type. The ID is created based on the Fortran name + call new_compiler(mpi_compiler,screen%s,'','',echo=.true.,verbose=.true.) + + ! Fortran match found! + if (mpi_compiler%id == compiler%id) then + which_one = i + return + end if + + case (LANG_C) + ! For other languages, we can only hope that the name matches the expected one + if (screen%s==compiler%cc) then + which_one = i + return + end if + case (LANG_CXX) + if (screen%s==compiler%cxx) then + which_one = i + return + end if + end select end do @@ -1029,17 +1078,18 @@ integer function mpi_compiler_match(wrappers,compiler,error) call fatal_error(error,trim(msg_out)) 1 format(' None out of ',i0,' valid MPI wrappers matches compiler ',a) -end function mpi_compiler_match +end subroutine mpi_compiler_match !> Return library version from the MPI wrapper command -type(version_t) function mpi_version_get(wrapper,error) +type(version_t) function mpi_version_get(mpilib,wrapper,error) + integer, intent(in) :: mpilib type(string_t), intent(in) :: wrapper type(error_t), allocatable, intent(out) :: error type(string_t) :: version_line ! Get version string - version_line = mpi_wrapper_query(wrapper,'version',error=error) + version_line = mpi_wrapper_query(mpilib,wrapper,'version',error=error) if (allocated(error)) return ! Wrap to object @@ -1074,13 +1124,14 @@ subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) fort_wrappers = [fort_wrappers,string_t('mpigfortran'),string_t('mpgfortran'),& string_t('mpig77'),string_t('mpg77')] - case (id_intel_classic_windows,id_intel_llvm_windows,& + case (id_intel_classic_windows,id_intel_llvm_windows, & id_intel_classic_nix,id_intel_classic_mac,id_intel_llvm_nix,id_intel_llvm_unknown) - c_wrappers = [c_wrappers,string_t(get_env('I_MPI_CC','mpiicc')),string_t('mpicl.bat')] - cpp_wrappers = [cpp_wrappers,string_t(get_env('I_MPI_CXX','mpiicpc')),string_t('mpicl.bat')] - fort_wrappers = [fort_wrappers,string_t(get_env('I_MPI_F90','mpiifort')),string_t('mpif77'),& - string_t('mpif90')] + print *, 'intel wrappers' + + c_wrappers = [string_t(get_env('I_MPI_CC','mpiicc'))] + cpp_wrappers = [string_t(get_env('I_MPI_CXX','mpiicpc'))] + fort_wrappers = [string_t(get_env('I_MPI_F90','mpiifort'))] case (id_pgi,id_nvhpc) @@ -1096,15 +1147,16 @@ subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) end select compiler_specific - call assert_mpi_wrappers(fort_wrappers) - call assert_mpi_wrappers(c_wrappers) - call assert_mpi_wrappers(cpp_wrappers) + call assert_mpi_wrappers(fort_wrappers,compiler) + call assert_mpi_wrappers(c_wrappers,compiler) + call assert_mpi_wrappers(cpp_wrappers,compiler) end subroutine mpi_wrappers !> Filter out invalid/unavailable mpi wrappers -subroutine assert_mpi_wrappers(wrappers,verbose) +subroutine assert_mpi_wrappers(wrappers,compiler,verbose) type(string_t), allocatable, intent(inout) :: wrappers(:) + type(compiler_t), intent(in) :: compiler logical, optional, intent(in) :: verbose integer :: i @@ -1113,7 +1165,8 @@ subroutine assert_mpi_wrappers(wrappers,verbose) allocate(works(size(wrappers))) do i=1,size(wrappers) - works(i) = which_mpi_library(wrappers(i),verbose) + print *, 'test wrapper <', wrappers(i)%s,'>' + works(i) = which_mpi_library(wrappers(i),compiler,verbose) end do ! Filter out non-working wrappers @@ -1154,7 +1207,7 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp end if ! Init command - command = wrapper%s + command = trim(wrapper%s) add_arguments: if (present(args)) then do iarg=1,size(args) @@ -1164,6 +1217,7 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp endif add_arguments if (echo_local) print *, '+ ', command + print *, '+ ', command ! Test command call execute_command_line(command//redirect_str,exitstat=stat,cmdstat=cmdstat) @@ -1202,18 +1256,26 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp end subroutine run_mpi_wrapper !> Get MPI library type from the wrapper command. Currently, only OpenMPI is supported -integer function which_mpi_library(wrapper,verbose) +integer function which_mpi_library(wrapper,compiler,verbose) type(string_t), intent(in) :: wrapper + type(compiler_t), intent(in) :: compiler logical, intent(in), optional :: verbose logical :: is_mpi_wrapper integer :: stat ! Run mpi wrapper first + print *, 'run wrapper ',wrapper%s call run_mpi_wrapper(wrapper,verbose=verbose,cmd_success=is_mpi_wrapper) if (is_mpi_wrapper) then + if (compiler%is_intel()) then + which_mpi_library = MPI_TYPE_INTEL + return + end if + + ! Init as currently unsupported library which_mpi_library = MPI_TYPE_NONE @@ -1244,7 +1306,8 @@ integer function which_mpi_library(wrapper,verbose) end function which_mpi_library !> Test if an MPI wrapper works -type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result(screen) +type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) result(screen) + integer, intent(in) :: mpilib type(string_t), intent(in) :: wrapper character(*), intent(in) :: command logical, intent(in), optional :: verbose @@ -1254,17 +1317,14 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( character(:), allocatable :: redirect_str,tokens(:) type(string_t) :: cmdstr type(compiler_t) :: mpi_compiler - integer :: stat,cmdstat,mpi,ire,length - - ! Get mpi type - mpi = which_mpi_library(wrapper,verbose) + integer :: stat,cmdstat,ire,length select case (command) ! Get MPI compiler name case ('compiler') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper @@ -1289,11 +1349,28 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Take out the first command from the whole line call split(screen%s,tokens,delimiters=' ') - screen%s = tokens(1) + screen%s = trim(tokens(1)) + + case (MPI_TYPE_INTEL) + + ! -show returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('-show')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local INTEL MPI library does not support -show') + return + end if + + ! Take out the first command from the whole line + call split(screen%s,tokens,delimiters=' ') + screen%s = trim(tokens(1)) + + print *, 'INTEL MPI compiler: ',screen%s case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select @@ -1302,7 +1379,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Get a list of additional compiler flags case ('flags') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper @@ -1336,9 +1413,29 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( screen%s = screen%s(len_trim(tokens(1))+1:) end if + case (MPI_TYPE_INTEL) + + call run_mpi_wrapper(wrapper,[string_t('-show')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local INTEL MPI library does not support -show') + return + end if + + ! MPICH reports the full command including the compiler name. Remove it if so + call remove_new_lines(screen) + call split(screen%s,tokens) + call new_compiler(mpi_compiler,tokens(1),tokens(1),tokens(1),echo=.false.,verbose=verbose) + + if (mpi_compiler%id/=id_unknown) then + ! Remove trailing compiler name + screen%s = screen%s(len_trim(tokens(1))+1:) + end if + case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select @@ -1346,7 +1443,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Get a list of additional linker flags case ('link') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI) ! --showme:link returns the linker command of this wrapper @@ -1382,7 +1479,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select @@ -1390,7 +1487,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Get a list of MPI library directories case ('link_dirs') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper @@ -1412,7 +1509,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Get a list of include directories for the MPI headers/modules case ('incl_dirs') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper @@ -1434,7 +1531,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Retrieve library version case ('version') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI) ! --showme:command returns the build command of this wrapper @@ -1500,9 +1597,40 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( end if + case (MPI_TYPE_INTEL) + + ! --showme:command returns the build command of this wrapper + call run_mpi_wrapper(wrapper,[string_t('-v')],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local INTEL MPI library does not support -v') + return + else + call remove_new_lines(screen) + end if + + print *, 'version screen = ',screen%s + + ! Extract version + ire = regex(screen%s,'\d+\.\d+\.\d+',length=length) + + print *, 'ire = ',ire,' length=',length + + if (ire>0 .and. length>0) then + + ! Parse version into the object (this should always work) + screen%s = screen%s(ire:ire+length-1) + + else + + call syntax_error(error,'cannot retrieve INTEL MPI library version.') + + end if + case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select @@ -1510,7 +1638,7 @@ type(string_t) function mpi_wrapper_query(wrapper,command,verbose,error) result( ! Get path to the MPI runner command case ('runner') - select case (mpi) + select case (mpilib) case (MPI_TYPE_OPENMPI,MPI_TYPE_MPICH,MPI_TYPE_MSMPI) call get_mpi_runner(screen,verbose,error) case default From ecc29164c035768f4fd5b031c881a80308b91eeb Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 05:51:37 -0500 Subject: [PATCH 388/799] bump fortran-regex to 1.1.2 due to ifort issue --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index c4a7e4fbbd..baf65056d6 100644 --- a/fpm.toml +++ b/fpm.toml @@ -15,7 +15,7 @@ toml-f.rev = "54686e45993f3a9a1d05d5c7419f39e7d5a4eb3f" M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" fortran-regex.git = "https://github.com/perazz/fortran-regex" -fortran-regex.tag = "1.1.1" +fortran-regex.tag = "1.1.2" jonquil.git = "https://github.com/toml-f/jonquil" jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" From f7387535a97a58833a27cebfe65ce8cebcc7ae44 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 05:57:48 -0500 Subject: [PATCH 389/799] add 'runner' task --- src/fpm_meta.f90 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index f246486dd4..614f34eded 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -794,10 +794,11 @@ subroutine get_mpi_runner(command,verbose,error) ! Try several commands do itri=1,size(try) - call find_command_location(trim(try(itri)),command%s,verbose=verbose,error=error) + call find_command_location(trim(try(itri)),command%s,verbose=.true.,error=error) + if (allocated(error)) cycle ! Success! - success = len_trim(command%s)>0 .and. .not.allocated(error) + success = len_trim(command%s) if (success) then command%s = join_path(command%s,trim(try(itri))) return @@ -1501,7 +1502,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select @@ -1523,7 +1524,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select @@ -1639,10 +1640,10 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) case ('runner') select case (mpilib) - case (MPI_TYPE_OPENMPI,MPI_TYPE_MPICH,MPI_TYPE_MSMPI) + case (MPI_TYPE_OPENMPI,MPI_TYPE_MPICH,MPI_TYPE_MSMPI,MPI_TYPE_INTEL) call get_mpi_runner(screen,verbose,error) case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' is not currently supported') + call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) return end select From 60a33a204367c396b4283966d597c5100506da6a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 06:19:46 -0500 Subject: [PATCH 390/799] simplify compiler path search --- src/fpm_meta.f90 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 614f34eded..c8f5509972 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -759,16 +759,19 @@ subroutine find_command_location(command,path,echo,verbose,error) return end if + print *, 'fullpath <'//fullpath//'>, command=<'//command//'>' + ! Extract path only length = index(fullpath,command,BACK=.true.) + print *, 'length=',length if (length<=0) then call fatal_error(error,'full path to command ('//command//') does not include command name') return elseif (length==1) then ! Compiler is in the current folder - call get_absolute_path('.',path,error) + path = '.' else - call get_absolute_path(fullpath(1:length-1),path,error) + path = fullpath(1:length-1) end if if (allocated(error)) return @@ -795,10 +798,16 @@ subroutine get_mpi_runner(command,verbose,error) ! Try several commands do itri=1,size(try) call find_command_location(trim(try(itri)),command%s,verbose=.true.,error=error) - if (allocated(error)) cycle + + if (allocated(error)) then + print *, 'error returned: ',error%message + cycle + end if + + print *, 'command = ',command%s ! Success! - success = len_trim(command%s) + success = len_trim(command%s)>0 if (success) then command%s = join_path(command%s,trim(try(itri))) return From c2d444b963fb8d073b0de8459fead4a1ecbdd4f5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 07:13:06 -0500 Subject: [PATCH 391/799] remove compiler prefix for C, C++ --- src/fpm_meta.f90 | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c8f5509972..09932ba418 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1274,8 +1274,10 @@ integer function which_mpi_library(wrapper,compiler,verbose) logical :: is_mpi_wrapper integer :: stat + ! Init as currently unsupported library + which_mpi_library = MPI_TYPE_NONE + ! Run mpi wrapper first - print *, 'run wrapper ',wrapper%s call run_mpi_wrapper(wrapper,verbose=verbose,cmd_success=is_mpi_wrapper) if (is_mpi_wrapper) then @@ -1285,10 +1287,6 @@ integer function which_mpi_library(wrapper,compiler,verbose) return end if - - ! Init as currently unsupported library - which_mpi_library = MPI_TYPE_NONE - ! Attempt to decipher which library this wrapper comes from. ! OpenMPI responds to '--showme' calls @@ -1307,10 +1305,6 @@ integer function which_mpi_library(wrapper,compiler,verbose) return endif - else - - which_mpi_library = MPI_TYPE_NONE - end if end function which_mpi_library @@ -1416,12 +1410,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) ! MPICH reports the full command including the compiler name. Remove it if so call remove_new_lines(screen) call split(screen%s,tokens) - call new_compiler(mpi_compiler,tokens(1),tokens(1),tokens(1),echo=.false.,verbose=verbose) - - if (mpi_compiler%id/=id_unknown) then - ! Remove trailing compiler name - screen%s = screen%s(len_trim(tokens(1))+1:) - end if + ! Remove trailing compiler name + screen%s = screen%s(len_trim(tokens(1))+1:) case (MPI_TYPE_INTEL) @@ -1436,12 +1426,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) ! MPICH reports the full command including the compiler name. Remove it if so call remove_new_lines(screen) call split(screen%s,tokens) - call new_compiler(mpi_compiler,tokens(1),tokens(1),tokens(1),echo=.false.,verbose=verbose) - - if (mpi_compiler%id/=id_unknown) then - ! Remove trailing compiler name - screen%s = screen%s(len_trim(tokens(1))+1:) - end if + ! Remove trailing compiler name + screen%s = screen%s(len_trim(tokens(1))+1:) case default @@ -1480,12 +1466,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) ! MPICH reports the full command including the compiler name. Remove it if so call remove_new_lines(screen) call split(screen%s,tokens) - call new_compiler(mpi_compiler,tokens(1),tokens(1),tokens(1),echo=.false.,verbose=verbose) - - if (mpi_compiler%id/=id_unknown) then - ! Remove trailing compiler name - screen%s = screen%s(len_trim(tokens(1))+1:) - end if + ! Remove trailing compiler name + screen%s = screen%s(len_trim(tokens(1))+1:) case default From 1cad824f4612e585ed58aeb30002470d00e4bfc7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 08:00:17 -0500 Subject: [PATCH 392/799] fix linker flags for ifort with c/c++ main program --- .../metapackage_mpi_cpp/app/main.cpp | 2 +- src/fpm_targets.f90 | 67 +++++++++++++------ 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/example_packages/metapackage_mpi_cpp/app/main.cpp b/example_packages/metapackage_mpi_cpp/app/main.cpp index 8203285a9e..45abe795a2 100644 --- a/example_packages/metapackage_mpi_cpp/app/main.cpp +++ b/example_packages/metapackage_mpi_cpp/app/main.cpp @@ -1,6 +1,6 @@ // Test MPI linking from a C main program -#include #include +#include using namespace std; diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index ddd34cd7d4..d04b5859b5 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -146,7 +146,7 @@ subroutine targets_from_sources(targets,model,prune,error) !> Enable tree-shaking/pruning of module dependencies logical, intent(in) :: prune - + !> Error structure type(error_t), intent(out), allocatable :: error @@ -240,14 +240,14 @@ subroutine build_target_list(targets,model) features = model%packages(j)%features, & macros = model%packages(j)%macros, & version = model%packages(j)%version) - + if (with_lib .and. sources(i)%unit_scope == FPM_SCOPE_LIB) then ! Archive depends on object call add_dependency(targets(1)%ptr, targets(size(targets))%ptr) end if - case (FPM_UNIT_CPPSOURCE) + case (FPM_UNIT_CPPSOURCE) call add_target(targets,package=model%packages(j)%name,source = sources(i), & type = FPM_TARGET_CPP_OBJECT, & @@ -307,6 +307,18 @@ subroutine build_target_list(targets,model) output_name = join_path(exe_dir, & sources(i)%exe_name//xsuffix)) + + ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option + ! -nofor-main to avoid "duplicate main" errors. + ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main + if (model%compiler%is_intel() .and. any(exe_type==[FPM_TARGET_C_OBJECT,FPM_TARGET_CPP_OBJECT])) then + if (get_os_type()==OS_WINDOWS) then + targets(size(targets))%ptr%compile_flags = '/nofor-main' + else + targets(size(targets))%ptr%compile_flags = '-nofor-main' + end if + end if + ! Executable depends on object call add_dependency(targets(size(targets))%ptr, targets(size(targets)-1)%ptr) @@ -385,7 +397,7 @@ subroutine collect_exe_link_dependencies(targets) dep%source%unit_type /= FPM_UNIT_MODULE .and. & index(dirname(dep%source%file_name), exe_source_dir) == 1) then - call add_dependency(exe, dep) + call add_dependency(exe, dep) end if @@ -583,13 +595,13 @@ subroutine prune_build_targets(targets, root_package) type(build_target_ptr), intent(inout), allocatable :: targets(:) !> Name of root package - character(*), intent(in) :: root_package + character(*), intent(in) :: root_package integer :: i, j, nexec type(string_t), allocatable :: modules_used(:) logical :: exclude_target(size(targets)) logical, allocatable :: exclude_from_archive(:) - + if (size(targets) < 1) then return end if @@ -599,7 +611,7 @@ subroutine prune_build_targets(targets, root_package) ! Enumerate modules used by executables, non-module subprograms and their dependencies do i=1,size(targets) - + if (targets(i)%ptr%target_type == FPM_TARGET_EXECUTABLE) then nexec = nexec + 1 @@ -620,16 +632,16 @@ subroutine prune_build_targets(targets, root_package) ! If there aren't any executables, then prune ! based on modules used in root package if (nexec < 1) then - + do i=1,size(targets) - + if (targets(i)%ptr%package_name == root_package .and. & targets(i)%ptr%target_type /= FPM_TARGET_ARCHIVE) then - + call collect_used_modules(targets(i)%ptr) - + end if - + end do end if @@ -651,11 +663,11 @@ subroutine prune_build_targets(targets, root_package) do j=1,size(target%source%modules_provided) if (target%source%modules_provided(j)%s .in. modules_used) then - + exclude_target(i) = .false. target%skip = .false. - end if + end if end do @@ -667,11 +679,11 @@ subroutine prune_build_targets(targets, root_package) do j=1,size(target%source%parent_modules) if (target%source%parent_modules(j)%s .in. modules_used) then - + exclude_target(i) = .false. target%skip = .false. - end if + end if end do @@ -684,7 +696,7 @@ subroutine prune_build_targets(targets, root_package) target%skip = .false. end if - end associate + end associate end do targets = pack(targets,.not.exclude_target) @@ -809,20 +821,33 @@ subroutine resolve_target_linking(targets, model) do i=1,size(targets) associate(target => targets(i)%ptr) + + ! May have been previously allocated + if (.not.allocated(target%compile_flags)) allocate(character(len=0) :: target%compile_flags) + + target%compile_flags = target%compile_flags//' ' + if (target%target_type /= FPM_TARGET_C_OBJECT .and. target%target_type /= FPM_TARGET_CPP_OBJECT) then - target%compile_flags = model%fortran_compile_flags & + target%compile_flags = target%compile_flags//model%fortran_compile_flags & & // get_feature_flags(model%compiler, target%features) else if (target%target_type == FPM_TARGET_C_OBJECT) then - target%compile_flags = model%c_compile_flags + target%compile_flags = target%compile_flags//model%c_compile_flags else if(target%target_type == FPM_TARGET_CPP_OBJECT) then - target%compile_flags = model%cxx_compile_flags + target%compile_flags = target%compile_flags//model%cxx_compile_flags + end if + + ! If the main program is a C/C++ one, Intel compilers require additional + ! linking flag -nofor-main to avoid a "duplicate main" error, see + ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main + if (model%compiler%is_intel() .and. target%target_type==FPM_TARGET_EXECUTABLE) then + print *, 'target compile flags ',target%compile_flags end if !> Get macros as flags. target%compile_flags = target%compile_flags // get_macros(model%compiler%id, & target%macros, & target%version) - + if (len(global_include_flags) > 0) then target%compile_flags = target%compile_flags//global_include_flags end if From 0258d936e37c51e11b5e0c7bd6bc4d712e33a021 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 08:06:25 -0500 Subject: [PATCH 393/799] fix intel compile flags for C/C++ main --- src/fpm_compiler.F90 | 26 ++++++++++++------ src/fpm_targets.f90 | 65 ++++++++++++++++++++++++++++++-------------- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 0b70d3ca2f..e105b8e729 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -106,6 +106,8 @@ module fpm_compiler procedure :: link !> Check whether compiler is recognized procedure :: is_unknown + !> Check whether compiler is Intel family + procedure :: is_intel !> Enumerate libraries, based on compiler and platform procedure :: enumerate_libraries end type compiler_t @@ -211,7 +213,7 @@ module fpm_compiler flag_cray_implicit_typing = " -el", & flag_cray_fixed_form = " -ffixed", & flag_cray_free_form = " -ffree" - + contains @@ -440,7 +442,7 @@ pure subroutine set_cpp_preprocessor_flags(id, flags) end subroutine set_cpp_preprocessor_flags -!> This function will parse and read the macros list and +!> This function will parse and read the macros list and !> return them as defined flags. function get_macros(id, macros_list, version) result(macros) integer(compiler_enum), intent(in) :: id @@ -450,7 +452,7 @@ function get_macros(id, macros_list, version) result(macros) character(len=:), allocatable :: macros character(len=:), allocatable :: macro_definition_symbol character(:), allocatable :: valued_macros(:) - + integer :: i @@ -473,10 +475,10 @@ function get_macros(id, macros_list, version) result(macros) end if do i = 1, size(macros_list) - + !> Split the macro name and value. call split(macros_list(i)%s, valued_macros, delimiters="=") - + if (size(valued_macros) > 1) then !> Check if the value of macro starts with '{' character. if (str_begins_with_str(trim(valued_macros(size(valued_macros))), "{")) then @@ -486,15 +488,15 @@ function get_macros(id, macros_list, version) result(macros) !> Check if the string contains "version" as substring. if (index(valued_macros(size(valued_macros)), "version") /= 0) then - + !> These conditions are placed in order to ensure proper spacing between the macros. macros = macros//macro_definition_symbol//trim(valued_macros(1))//'='//version cycle end if end if - end if + end if end if - + macros = macros//macro_definition_symbol//macros_list(i)%s end do @@ -885,6 +887,12 @@ pure function is_unknown(self) is_unknown = self%id == id_unknown end function is_unknown +pure logical function is_intel(self) + class(compiler_t), intent(in) :: self + is_intel = any(self%id == [id_intel_classic_mac,id_intel_classic_nix,id_intel_classic_windows,& + id_intel_llvm_nix,id_intel_llvm_unknown,id_intel_llvm_windows]) +end function is_intel + !> !> Enumerate libraries, based on compiler and platform !> @@ -919,7 +927,7 @@ subroutine new_compiler(self, fc, cc, cxx, echo, verbose) logical, intent(in) :: verbose self%id = get_compiler_id(fc) - + self%echo = echo self%verbose = verbose self%fc = fc diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index ae1f120296..d04b5859b5 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -240,14 +240,14 @@ subroutine build_target_list(targets,model) features = model%packages(j)%features, & macros = model%packages(j)%macros, & version = model%packages(j)%version) - + if (with_lib .and. sources(i)%unit_scope == FPM_SCOPE_LIB) then ! Archive depends on object call add_dependency(targets(1)%ptr, targets(size(targets))%ptr) end if - case (FPM_UNIT_CPPSOURCE) + case (FPM_UNIT_CPPSOURCE) call add_target(targets,package=model%packages(j)%name,source = sources(i), & type = FPM_TARGET_CPP_OBJECT, & @@ -307,6 +307,18 @@ subroutine build_target_list(targets,model) output_name = join_path(exe_dir, & sources(i)%exe_name//xsuffix)) + + ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option + ! -nofor-main to avoid "duplicate main" errors. + ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main + if (model%compiler%is_intel() .and. any(exe_type==[FPM_TARGET_C_OBJECT,FPM_TARGET_CPP_OBJECT])) then + if (get_os_type()==OS_WINDOWS) then + targets(size(targets))%ptr%compile_flags = '/nofor-main' + else + targets(size(targets))%ptr%compile_flags = '-nofor-main' + end if + end if + ! Executable depends on object call add_dependency(targets(size(targets))%ptr, targets(size(targets)-1)%ptr) @@ -385,7 +397,7 @@ subroutine collect_exe_link_dependencies(targets) dep%source%unit_type /= FPM_UNIT_MODULE .and. & index(dirname(dep%source%file_name), exe_source_dir) == 1) then - call add_dependency(exe, dep) + call add_dependency(exe, dep) end if @@ -583,13 +595,13 @@ subroutine prune_build_targets(targets, root_package) type(build_target_ptr), intent(inout), allocatable :: targets(:) !> Name of root package - character(*), intent(in) :: root_package + character(*), intent(in) :: root_package integer :: i, j, nexec type(string_t), allocatable :: modules_used(:) logical :: exclude_target(size(targets)) logical, allocatable :: exclude_from_archive(:) - + if (size(targets) < 1) then return end if @@ -599,7 +611,7 @@ subroutine prune_build_targets(targets, root_package) ! Enumerate modules used by executables, non-module subprograms and their dependencies do i=1,size(targets) - + if (targets(i)%ptr%target_type == FPM_TARGET_EXECUTABLE) then nexec = nexec + 1 @@ -620,16 +632,16 @@ subroutine prune_build_targets(targets, root_package) ! If there aren't any executables, then prune ! based on modules used in root package if (nexec < 1) then - + do i=1,size(targets) - + if (targets(i)%ptr%package_name == root_package .and. & targets(i)%ptr%target_type /= FPM_TARGET_ARCHIVE) then - + call collect_used_modules(targets(i)%ptr) - + end if - + end do end if @@ -651,11 +663,11 @@ subroutine prune_build_targets(targets, root_package) do j=1,size(target%source%modules_provided) if (target%source%modules_provided(j)%s .in. modules_used) then - + exclude_target(i) = .false. target%skip = .false. - end if + end if end do @@ -667,11 +679,11 @@ subroutine prune_build_targets(targets, root_package) do j=1,size(target%source%parent_modules) if (target%source%parent_modules(j)%s .in. modules_used) then - + exclude_target(i) = .false. target%skip = .false. - end if + end if end do @@ -684,7 +696,7 @@ subroutine prune_build_targets(targets, root_package) target%skip = .false. end if - end associate + end associate end do targets = pack(targets,.not.exclude_target) @@ -809,20 +821,33 @@ subroutine resolve_target_linking(targets, model) do i=1,size(targets) associate(target => targets(i)%ptr) + + ! May have been previously allocated + if (.not.allocated(target%compile_flags)) allocate(character(len=0) :: target%compile_flags) + + target%compile_flags = target%compile_flags//' ' + if (target%target_type /= FPM_TARGET_C_OBJECT .and. target%target_type /= FPM_TARGET_CPP_OBJECT) then - target%compile_flags = model%fortran_compile_flags & + target%compile_flags = target%compile_flags//model%fortran_compile_flags & & // get_feature_flags(model%compiler, target%features) else if (target%target_type == FPM_TARGET_C_OBJECT) then - target%compile_flags = model%c_compile_flags + target%compile_flags = target%compile_flags//model%c_compile_flags else if(target%target_type == FPM_TARGET_CPP_OBJECT) then - target%compile_flags = model%cxx_compile_flags + target%compile_flags = target%compile_flags//model%cxx_compile_flags + end if + + ! If the main program is a C/C++ one, Intel compilers require additional + ! linking flag -nofor-main to avoid a "duplicate main" error, see + ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main + if (model%compiler%is_intel() .and. target%target_type==FPM_TARGET_EXECUTABLE) then + print *, 'target compile flags ',target%compile_flags end if !> Get macros as flags. target%compile_flags = target%compile_flags // get_macros(model%compiler%id, & target%macros, & target%version) - + if (len(global_include_flags) > 0) then target%compile_flags = target%compile_flags//global_include_flags end if From 33222889c7db8f5a5fe7fd22a26a799247d82d22 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 08:06:59 -0500 Subject: [PATCH 394/799] cleanup --- src/fpm_targets.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index d04b5859b5..c7542e85f6 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -307,7 +307,6 @@ subroutine build_target_list(targets,model) output_name = join_path(exe_dir, & sources(i)%exe_name//xsuffix)) - ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option ! -nofor-main to avoid "duplicate main" errors. ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main @@ -839,9 +838,6 @@ subroutine resolve_target_linking(targets, model) ! If the main program is a C/C++ one, Intel compilers require additional ! linking flag -nofor-main to avoid a "duplicate main" error, see ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main - if (model%compiler%is_intel() .and. target%target_type==FPM_TARGET_EXECUTABLE) then - print *, 'target compile flags ',target%compile_flags - end if !> Get macros as flags. target%compile_flags = target%compile_flags // get_macros(model%compiler%id, & From dfafcf3cdaa02dc6758f6f2226b8057129287e40 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 08:15:57 -0500 Subject: [PATCH 395/799] improve output message --- src/fpm/manifest/build.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index b3af26e517..ac8dad8473 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -160,8 +160,9 @@ subroutine check(table, package_name, error) continue case default - call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in [build]"//& - " building package "//package_name) + + call syntax_error(error, 'Manifest file syntax error: key "'//list(ikey)%key//'" found in the [build] '//& + 'section of package/dependency "'//package_name//'" fpm.toml is not allowed') exit end select From f6e93ec50e4b159ecf86a9aabcbc8ca8a1322875 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 18:00:20 +0200 Subject: [PATCH 396/799] introduce ubuntu+intelmpi --- .github/workflows/meta.yml | 19 ++++++++++++++++++- src/fpm_meta.f90 | 29 +++++++++++------------------ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 9e99df547f..de013716ed 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,9 +28,11 @@ jobs: fail-fast: false matrix: os: [macos-11,windows-latest,ubuntu-latest] - mpi: [mpich,openmpi,msmpi] + mpi: [mpich,openmpi,msmpi,intel] gcc_v: [10] # Version of GFortran we want to use exclude: + - os: macos-11 + mpi: intel - os: macos-11 mpi: msmpi - os: macos-11 # temporary @@ -41,8 +43,16 @@ jobs: mpi: msmpi - os: windows-latest mpi: mpich + - os: windows-latest + mpi: intel - os: windows-latest mpi: openmpi + - os: ubuntu-latest + mpi: openmpi + - os: ubuntu-latest + mpi: mpich + - os: ubuntu-latest + mpi: msmpi include: - os: macos-11 os-arch: macos-x86_64 @@ -73,6 +83,13 @@ jobs: curl gcc-fortran + - name: (Ubuntu) Install INTEL MPI toolchain + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') + uses: awvwgk/setup-fortran@v1 + with: + compiler: intel-classic + version: 2021.8 + - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 09932ba418..302ca0796a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -440,19 +440,13 @@ subroutine init_mpi(this,compiler,error) !> Cleanup call destroy(this) - print *, 'init wrappers' !> Get all candidate MPI wrappers call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) if (verbose) print 1, size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) - print *, 'wrapper compiler fit' - call wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,wcfit,mpilib,error) - print *, 'wcfit = ',wcfit - print *, 'mpilib = ',mpilib - if (allocated(error) .or. all(wcfit==0)) then !> No wrapper compiler fit. Are we on Windows? use MSMPI-specific search @@ -471,8 +465,6 @@ subroutine init_mpi(this,compiler,error) if (wcfit(LANG_C)>0) cwrap = c_wrappers (wcfit(LANG_C)) if (wcfit(LANG_CXX)>0) cxxwrap = cpp_wrappers (wcfit(LANG_CXX)) - print *, 'wcfit' - !> If there's only an available Fortran wrapper, and the compiler's different than fpm's baseline !> fortran compiler suite, we still want to enable C language flags as that is most likely being !> ABI-compatible anyways. However, issues may arise. @@ -516,19 +508,20 @@ subroutine wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,w !> Were any wrappers found? has_wrappers = size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)>0 - if (has_wrappers) then + if (size(fort_wrappers)>0) & + call mpi_compiler_match(LANG_FORTRAN,fort_wrappers,compiler,wrap(LANG_FORTRAN),mpi(LANG_FORTRAN),wrap_error) - !> Find a Fortran wrapper for the current compiler - call mpi_compiler_match(LANG_FORTRAN,fort_wrappers,compiler,wrap(LANG_FORTRAN),mpi(LANG_FORTRAN),wrap_error) - call mpi_compiler_match(LANG_C, c_wrappers,compiler,wrap(LANG_C),mpi(LANG_C),wrap_error) - call mpi_compiler_match(LANG_CXX, cpp_wrappers,compiler,wrap(LANG_CXX),mpi(LANG_CXX),wrap_error) + if (size(c_wrappers)>0) & + call mpi_compiler_match(LANG_C,c_wrappers,compiler,wrap(LANG_C),mpi(LANG_C),wrap_error) - if (all(wrap==0)) then - call fatal_error(error,'no valid wrappers match current compiler, '//compiler_name(compiler)) - return - end if + if (size(cpp_wrappers)>0) & + call mpi_compiler_match(LANG_CXX,cpp_wrappers,compiler,wrap(LANG_CXX),mpi(LANG_CXX),wrap_error) - endif + !> Find a Fortran wrapper for the current compiler + if (all(wrap==0)) then + call fatal_error(error,'no valid wrappers match current compiler, '//compiler_name(compiler)) + return + end if end subroutine wrapper_compiler_fit From 13850e677013598f5087ca370c7c12401275e87a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 18:23:31 +0200 Subject: [PATCH 397/799] install mpi sdk --- .github/workflows/meta.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index de013716ed..a1903cef2d 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -83,13 +83,18 @@ jobs: curl gcc-fortran - - name: (Ubuntu) Install INTEL MPI toolchain + - name: (Ubuntu) Install Intel toolchain if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') uses: awvwgk/setup-fortran@v1 with: compiler: intel-classic version: 2021.8 + - name: (Ubuntu) Install MPI SDK + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') + run: DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates build-essential pkg-config gnupg libarchive13 openssh-server openssh-client wget net-tools git intel-basekit-getting-started intel-oneapi-advisor intel-oneapi-ccl-devel intel-oneapi-common-licensing intel-oneapi-common-vars intel-oneapi-compiler-dpcpp-cpp intel-oneapi-dal-devel intel-oneapi-dev-utilities intel-oneapi-dnnl-devel intel-oneapi-dpcpp-debugger intel-oneapi-ipp-devel intel-oneapi-ippcp-devel intel-oneapi-libdpstd-devel intel-oneapi-mkl-devel intel-oneapi-tbb-devel intel-oneapi-vtune intel-level-zero-gpu level-zero intel-hpckit-getting-started intel-oneapi-clck intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic intel-oneapi-compiler-fortran intel-oneapi-inspector intel-oneapi-itac intel-oneapi-mpi-devel + + - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 From f2aec4b9460f26133becb5386ecd2a06cc904f9c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 18:28:37 +0200 Subject: [PATCH 398/799] install as sudo --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index a1903cef2d..c324cd0e44 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -92,7 +92,7 @@ jobs: - name: (Ubuntu) Install MPI SDK if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') - run: DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates build-essential pkg-config gnupg libarchive13 openssh-server openssh-client wget net-tools git intel-basekit-getting-started intel-oneapi-advisor intel-oneapi-ccl-devel intel-oneapi-common-licensing intel-oneapi-common-vars intel-oneapi-compiler-dpcpp-cpp intel-oneapi-dal-devel intel-oneapi-dev-utilities intel-oneapi-dnnl-devel intel-oneapi-dpcpp-debugger intel-oneapi-ipp-devel intel-oneapi-ippcp-devel intel-oneapi-libdpstd-devel intel-oneapi-mkl-devel intel-oneapi-tbb-devel intel-oneapi-vtune intel-level-zero-gpu level-zero intel-hpckit-getting-started intel-oneapi-clck intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic intel-oneapi-compiler-fortran intel-oneapi-inspector intel-oneapi-itac intel-oneapi-mpi-devel + run: DEBIAN_FRONTEND=noninteractive sudo apt-get install -y --no-install-recommends ca-certificates build-essential pkg-config gnupg libarchive13 openssh-server openssh-client wget net-tools git intel-basekit-getting-started intel-oneapi-advisor intel-oneapi-ccl-devel intel-oneapi-common-licensing intel-oneapi-common-vars intel-oneapi-compiler-dpcpp-cpp intel-oneapi-dal-devel intel-oneapi-dev-utilities intel-oneapi-dnnl-devel intel-oneapi-dpcpp-debugger intel-oneapi-ipp-devel intel-oneapi-ippcp-devel intel-oneapi-libdpstd-devel intel-oneapi-mkl-devel intel-oneapi-tbb-devel intel-oneapi-vtune intel-level-zero-gpu level-zero intel-hpckit-getting-started intel-oneapi-clck intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic intel-oneapi-compiler-fortran intel-oneapi-inspector intel-oneapi-itac intel-oneapi-mpi-devel - name: (Windows) Put MSYS2_MinGW64 on PATH From 048995c17b9f3819b4ae2ac52d8c4ea394b8d313 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 13:06:46 +0700 Subject: [PATCH 399/799] Always register when bool is injected --- src/fpm/manifest.f90 | 2 +- src/fpm_os.F90 | 22 ++++++---------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 3b3d42d79d..796cd37c0b 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -208,7 +208,7 @@ subroutine add_fpm_is_windows_macro(preprocessors, is_unix) is_unix_os = os_is_unix() end if - if (is_unix_os) return + if (is_unix_os .and. .not. present(is_unix)) return if (allocated(preprocessors)) then do i = 1, size(preprocessors) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index e3a7fd166c..dc4511ac0a 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -168,48 +168,38 @@ subroutine get_absolute_path(path, absolute_path, error) character(len=:), allocatable :: home if (len_trim(path) < 1) then - ! Empty path - call fatal_error(error, 'Path cannot be empty') - return + call fatal_error(error, 'Path cannot be empty'); return else if (path(1:1) == '~') then - ! Expand home call get_home(home, error) if (allocated(error)) return if (len_trim(path) == 1) then - absolute_path = home - return + absolute_path = home; return end if if (os_is_unix()) then if (path(2:2) /= '/') then - call fatal_error(error, "Wrong separator in path: '"//path//"'") - return + call fatal_error(error, "Wrong separator in path: '"//path//"'"); return end if else if (path(2:2) /= '\') then - call fatal_error(error, "Wrong separator in path: '"//path//"'") - return + call fatal_error(error, "Wrong separator in path: '"//path//"'"); return end if end if if (len_trim(path) == 2) then - absolute_path = home - return + absolute_path = home; return end if absolute_path = join_path(home, path(3:len_trim(path))) if (.not. exists(absolute_path)) then - call fatal_error(error, "Path not found: '"//absolute_path//"'") - deallocate (absolute_path) - return + call fatal_error(error, "Path not found: '"//absolute_path//"'"); return end if else ! Get canonicalized absolute path from either the absolute or the relative path. call get_realpath(path, absolute_path, error) end if - end subroutine !> Converts a path to an absolute, canonical path. From fd70bdcc488be132444ee933cf6b261cc4235724 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 13:46:59 +0700 Subject: [PATCH 400/799] Use C routine again if not bootstrapping --- src/fpm_os.F90 | 18 +++++++++++++++++- src/fpm_os.c | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/fpm_os.c diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index dc4511ac0a..0a5784edaa 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -55,6 +55,18 @@ function fullpath(resolved_path, path, maxLength) result(ptr) bind(C, name="_ful integer(c_int), value, intent(in) :: maxLength type(c_ptr) :: ptr end function fullpath + + !> Determine the absolute, canonicalized path for a given path. + !> Calls custom C routine because the `_WIN32` macro is correctly exported + !> in C using `gfortran`. + function c_realpath(path, resolved_path, maxLength) result(ptr) & + bind(C, name="c_realpath") + import :: c_ptr, c_char, c_int + character(kind=c_char, len=1), intent(in) :: path(*) + character(kind=c_char, len=1), intent(out) :: resolved_path(*) + integer(c_int), value, intent(in) :: maxLength + type(c_ptr) :: ptr + end function c_realpath end interface contains @@ -143,11 +155,15 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) -! The _WIN32 macro is currently not exported using gfortran. Therefore using FPM_IS_WINDOWS. +#ifndef FPM_BOOTSTRAP + ! Use C routine if not in bootstrap mode. + ptr = c_realpath(appended_path, cpath, buffersize) +#else #ifndef FPM_IS_WINDOWS ptr = realpath(appended_path, cpath) #else ptr = fullpath(cpath, appended_path, buffersize) +#endif #endif if (c_associated(ptr)) then diff --git a/src/fpm_os.c b/src/fpm_os.c new file mode 100644 index 0000000000..2d417a0695 --- /dev/null +++ b/src/fpm_os.c @@ -0,0 +1,16 @@ +#include + +/// @brief Determine the absolute, canonicalized path for a given path. +/// @param path +/// @param resolved_path +/// @param maxLength +/// @return +int c_realpath(char* path, char* resolved_path, int maxLength) { +// Checking macro in C because it doesn't work with gfortran on Windows, even +// when exported manually. +#ifndef _WIN32 + return realpath(path, resolved_path); +#else + return _fullpath(resolved_path, path, maxLength); +#endif +} From b226ffbe18c0bd03f2d934557a58b61fcd1fb521 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 14:12:45 +0700 Subject: [PATCH 401/799] Try to fix test --- test/fpm_test/test_manifest.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 20283efe6d..9c4ffed5bd 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1333,7 +1333,7 @@ subroutine test_macro_parsing(error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package - character(:), allocatable :: temp_file + character(:), allocatable :: temp_file, macros integer :: unit integer(compiler_enum) :: id @@ -1352,8 +1352,10 @@ subroutine test_macro_parsing(error) if (allocated(error)) return - if (get_macros(id, package%preprocess(1)%macros, package%version%s()) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then - call test_failed(error, "Macros were not parsed correctly") + macros = get_macros(id, package%preprocess(1)%macros, package%version%s()) + + if (macros /= " -DFOO -DBAR=2 -DVERSION=0.1.0 -DFPM_IS_WINDOWS") then + call test_failed(error, "Macros were not parsed correctly: '"//macros//"'") end if end subroutine test_macro_parsing From 59aae56966e0d01a727bb8bf501fc53ebb2dccb0 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 14:18:13 +0700 Subject: [PATCH 402/799] Not append on unix --- test/fpm_test/test_manifest.f90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 9c4ffed5bd..455bf7d3fd 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1,6 +1,7 @@ !> Define tests for the `fpm_manifest` modules module test_manifest use fpm_filesystem, only: get_temp_filename + use fpm_environment, only: os_is_unix use testsuite, only : new_unittest, unittest_t, error_t, test_failed, check_string use fpm_manifest use fpm_manifest_profile, only: profile_config_t, find_profile @@ -1333,7 +1334,7 @@ subroutine test_macro_parsing(error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package - character(:), allocatable :: temp_file, macros + character(:), allocatable :: temp_file, macros, expected_result integer :: unit integer(compiler_enum) :: id @@ -1354,7 +1355,10 @@ subroutine test_macro_parsing(error) macros = get_macros(id, package%preprocess(1)%macros, package%version%s()) - if (macros /= " -DFOO -DBAR=2 -DVERSION=0.1.0 -DFPM_IS_WINDOWS") then + expected_result = " -DFOO -DBAR=2 -DVERSION=0.1.0" + if (.not. os_is_unix()) expected_result = expected_result // " -DFPM_IS_WINDOWS" + + if (macros /= expected_result) then call test_failed(error, "Macros were not parsed correctly: '"//macros//"'") end if From 89d35d51c669c7d5514620aad6abf4aea75de72c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 14:34:20 +0700 Subject: [PATCH 403/799] Remove unused variable --- src/fpm/manifest.f90 | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 796cd37c0b..41810677c8 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -191,24 +191,14 @@ end subroutine package_defaults !> Add the FPM_IS_WINDOWS macro if it wasn't already defined. - subroutine add_fpm_is_windows_macro(preprocessors, is_unix) + subroutine add_fpm_is_windows_macro(preprocessors) !> Preprocessor configurations. type(preprocess_config_t), allocatable, intent(inout) :: preprocessors(:) - !> Whether the operating system is Unix-like. - logical, intent(in), optional :: is_unix - type(preprocess_config_t) :: new_cpp integer :: i, j - logical :: is_unix_os = .true. - - if (present(is_unix)) then - is_unix_os = is_unix - else - is_unix_os = os_is_unix() - end if - if (is_unix_os .and. .not. present(is_unix)) return + if (os_is_unix()) return if (allocated(preprocessors)) then do i = 1, size(preprocessors) From 86dedadd5911bfe0707b6d46ef13a5128e0bb09a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 15:23:22 +0700 Subject: [PATCH 404/799] Add tests --- test/fpm_test/test_manifest.f90 | 204 +++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 455bf7d3fd..d873d0673f 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -69,7 +69,13 @@ subroutine collect_manifest(tests) & new_unittest("preprocess-wrongkey", test_preprocess_wrongkey, should_fail=.true.), & & new_unittest("preprocessors-empty", test_preprocessors_empty, should_fail=.true.), & & new_unittest("macro-parsing", test_macro_parsing, should_fail=.false.), & - & new_unittest("macro-parsing-dependency", test_macro_parsing_dependency, should_fail=.false.)] + & new_unittest("macro-parsing-dependency", test_macro_parsing_dependency, should_fail=.false.), & + & new_unittest("add-windows-macro-to-empty", test_add_windows_macro_to_empty), & + & new_unittest("add-windows-macro-to-preprocess", test_add_windows_macro_to_preprocess), & + & new_unittest("add-windows-macro-to-empty-macros", test_add_windows_macro_to_empty_macros), & + & new_unittest("add-windows-macro-to-other-macro", test_add_windows_macro_to_other_macro), & + & new_unittest("add-second-windows-macro", test_add_second_windows_macro) & + & ] end subroutine collect_manifest @@ -1422,4 +1428,200 @@ subroutine test_macro_parsing_dependency(error) end subroutine test_macro_parsing_dependency + !> Add `FPM_IS_WINDOWS` macro when no preprocess table exists. + subroutine test_add_windows_macro_to_empty(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & 'version = "0.1.0"' + close(unit) + + call get_package_data(package, temp_file, error) + if (allocated(error)) return + + if (os_is_unix()) then + if (allocated(package%preprocess)) call test_failed(error, 'Preprocess table should not be allocated.') + else + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, 'No cpp table created.') + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then + call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") + end if + end if + end + + !> Add `FPM_IS_WINDOWS` macro to empty cpp table. + subroutine test_add_windows_macro_to_preprocess(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & 'version = "0.1.0"', & + & '[preprocess]', & + & '[preprocess.cpp]' + close(unit) + + call get_package_data(package, temp_file, error) + if (allocated(error)) return + + if (os_is_unix()) then + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should not be allocated.") + else + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then + call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") + end if + end if + end + + !> Add `FPM_IS_WINDOWS` macro to empty macros table. + subroutine test_add_windows_macro_to_empty_macros(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & 'version = "0.1.0"', & + & '[preprocess]', & + & '[preprocess.cpp]', & + & 'macros = []' + close(unit) + + call get_package_data(package, temp_file, error) + if (allocated(error)) return + + if (os_is_unix()) then + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should be allocated.") + if (size(package%preprocess(1)%macros) /= 0) call test_failed(error, "Macros not empty.") + else + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then + call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") + end if + end if + end + + !> Add `FPM_IS_WINDOWS` macro to list of macros containing another macro. + subroutine test_add_windows_macro_to_other_macro(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & 'version = "0.1.0"', & + & '[preprocess]', & + & '[preprocess.cpp]', & + & 'macros = ["ABC"]' + close(unit) + + call get_package_data(package, temp_file, error) + if (allocated(error)) return + + if (os_is_unix()) then + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should be allocated.") + if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Wrong number of macros.") + else + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (size(package%preprocess(1)%macros) /= 2) call test_failed(error, "Wrong number of macros.") + if (package%preprocess(1)%macros(2)%s /= 'FPM_IS_WINDOWS') then + call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") + end if + end if + end + + !> Add `FPM_IS_WINDOWS` macro to list of macros that already contains "FPM_IS_WINDOWS". + subroutine test_add_second_windows_macro(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & 'version = "0.1.0"', & + & '[preprocess]', & + & '[preprocess.cpp]', & + & 'macros = ["FPM_IS_WINDOWS"]' + close(unit) + + call get_package_data(package, temp_file, error) + if (allocated(error)) return + + if (os_is_unix()) then + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should be allocated.") + if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Wrong number of macros.") + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, '"FPM_IS_WINDOWS" not parsed.') + else + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Macro should not have been added.") + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, '"FPM_IS_WINDOWS" should exist.') + end if + end + end module test_manifest From d4f8194f089816e04b0209dc3c4d1c4a871410dd Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 15:30:22 +0700 Subject: [PATCH 405/799] Remove duplications --- test/fpm_test/test_manifest.f90 | 65 +++++++++++---------------------- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index d873d0673f..31cf63fbc9 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1484,15 +1484,13 @@ subroutine test_add_windows_macro_to_preprocess(error) call get_package_data(package, temp_file, error) if (allocated(error)) return + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (os_is_unix()) then - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") if (allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should not be allocated.") else - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") @@ -1524,20 +1522,15 @@ subroutine test_add_windows_macro_to_empty_macros(error) call get_package_data(package, temp_file, error) if (allocated(error)) return + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (os_is_unix()) then - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should be allocated.") if (size(package%preprocess(1)%macros) /= 0) call test_failed(error, "Macros not empty.") else - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then - call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") - end if + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") end if end @@ -1565,21 +1558,16 @@ subroutine test_add_windows_macro_to_other_macro(error) call get_package_data(package, temp_file, error) if (allocated(error)) return + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (os_is_unix()) then - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should be allocated.") if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Wrong number of macros.") else - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') if (size(package%preprocess(1)%macros) /= 2) call test_failed(error, "Wrong number of macros.") - if (package%preprocess(1)%macros(2)%s /= 'FPM_IS_WINDOWS') then - call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") - end if + if (package%preprocess(1)%macros(2)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") end if end @@ -1607,21 +1595,12 @@ subroutine test_add_second_windows_macro(error) call get_package_data(package, temp_file, error) if (allocated(error)) return - if (os_is_unix()) then - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should be allocated.") - if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Wrong number of macros.") - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, '"FPM_IS_WINDOWS" not parsed.') - else - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Macro should not have been added.") - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, '"FPM_IS_WINDOWS" should exist.') - end if + if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') + if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') + if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') + if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Macro should not have been added.") + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, '"FPM_IS_WINDOWS" should exist.') end end module test_manifest From ef2b4c24d9b726fa8e39e17555a86903d02ac3d6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 28 Apr 2023 15:31:58 +0700 Subject: [PATCH 406/799] Shorten --- test/fpm_test/test_manifest.f90 | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 31cf63fbc9..b1e6eebb33 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1455,9 +1455,7 @@ subroutine test_add_windows_macro_to_empty(error) if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') if (package%preprocess(1)%name /= 'cpp') call test_failed(error, 'No cpp table created.') if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then - call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") - end if + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") end if end @@ -1492,9 +1490,7 @@ subroutine test_add_windows_macro_to_preprocess(error) if (allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should not be allocated.") else if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') then - call test_failed(error, "'FPM_IS_WINDOWS' macro not added.") - end if + if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") end if end From 616a5dc4e82a433845463f7a89e971b3cf88ef83 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 29 Apr 2023 13:35:23 +0700 Subject: [PATCH 407/799] Assign later --- src/fpm.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index c70b420a46..3888b817ae 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -41,8 +41,8 @@ subroutine build_model(model, settings, package, error) integer :: i, j type(package_config_t) :: dependency character(len=:), allocatable :: manifest, lib_dir, flags, cflags, cxxflags, ldflags - logical :: has_cpp = .false. - logical :: duplicates_found = .false. + logical :: has_cpp + logical :: duplicates_found type(string_t) :: include_dir model%package_name = package%name @@ -96,6 +96,7 @@ subroutine build_model(model, settings, package, error) allocate(model%packages(model%deps%ndep)) + has_cpp = .false. do i = 1, model%deps%ndep associate(dep => model%deps%dep(i)) manifest = join_path(dep%proj_dir, "fpm.toml") @@ -247,6 +248,7 @@ subroutine build_model(model, settings, package, error) if (allocated(error)) return ! Check for duplicate modules + duplicates_found = .false. call check_modules_for_duplicates(model, duplicates_found) if (duplicates_found) then call fpm_stop(1,'*build_model*:Error: One or more duplicate module names found.') From c457351d8f3684bfbde7c0dc07f0f9ad331d0652 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 29 Apr 2023 15:28:40 +0700 Subject: [PATCH 408/799] Assign later --- src/fpm/manifest.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 41810677c8..70e0802efa 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -109,7 +109,7 @@ subroutine get_package_data(package, file, error, apply_defaults, add_is_windows type(toml_table), allocatable :: table character(len=:), allocatable :: root - logical :: set_is_windows_macro = .true. + logical :: set_is_windows_macro call read_package_file(table, file, error) if (allocated(error)) return @@ -131,6 +131,7 @@ subroutine get_package_data(package, file, error, apply_defaults, add_is_windows end if end if + set_is_windows_macro = .true. if (present(add_is_windows_macro)) set_is_windows_macro = add_is_windows_macro if (set_is_windows_macro) call add_fpm_is_windows_macro(package%preprocess) From 90cfb2e48654fb2c9f91470cd7aa80fb85f8f78a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 27 Apr 2023 09:03:34 -0500 Subject: [PATCH 409/799] move flags to `compiler_t` move main flags to `compiler_t` fix char allocation debugging prints default init use subroutine, not function no fortran no empty string use temporary variable use associate --- src/fpm_compiler.F90 | 41 +++++++++++++++++++++++++++++++++++++++++ src/fpm_targets.f90 | 29 ++++++++++++++++------------- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index e105b8e729..e46032503d 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -96,6 +96,8 @@ module fpm_compiler procedure :: get_include_flag !> Get feature flag procedure :: get_feature_flag + !> Get flags for the main linking command + procedure :: get_main_flags !> Compile a Fortran object procedure :: compile_fortran !> Compile a C object @@ -666,6 +668,45 @@ function get_feature_flag(self, feature) result(flags) end function get_feature_flag +!> Get special flags for the main linker +subroutine get_main_flags(self, language, flags) + class(compiler_t), intent(in) :: self + character(len=*), intent(in) :: language + character(len=:), allocatable, intent(out) :: flags + + flags = "" + select case(language) + + case("fortran") + flags = "" + + case("c") + + ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option + ! -nofor-main to avoid "duplicate main" errors. + ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main + select case(self%id) + case(id_intel_classic_nix, id_intel_classic_mac, id_intel_llvm_nix) + flags = '-nofor-main' + case(id_intel_classic_windows,id_intel_llvm_windows) + flags = '/nofor-main' + end select + + case("c++","cpp","cxx") + + select case(self%id) + case(id_intel_classic_nix, id_intel_classic_mac, id_intel_llvm_nix) + flags = '-nofor-main' + case(id_intel_classic_windows,id_intel_llvm_windows) + flags = '/nofor-main' + end select + + case default + error stop "Unknown language '"//language//'", try "fortran", "c", "c++"' + end select + +end subroutine get_main_flags + subroutine get_default_c_compiler(f_compiler, c_compiler) character(len=*), intent(in) :: f_compiler character(len=:), allocatable, intent(out) :: c_compiler diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index c7542e85f6..21cda7403a 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -194,7 +194,7 @@ subroutine build_target_list(targets,model) type(fpm_model_t), intent(inout), target :: model integer :: i, j, n_source, exe_type - character(:), allocatable :: xsuffix, exe_dir + character(:), allocatable :: xsuffix, exe_dir, compile_flags logical :: with_lib ! Check for empty build (e.g. header-only lib) @@ -307,25 +307,28 @@ subroutine build_target_list(targets,model) output_name = join_path(exe_dir, & sources(i)%exe_name//xsuffix)) - ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option - ! -nofor-main to avoid "duplicate main" errors. - ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main - if (model%compiler%is_intel() .and. any(exe_type==[FPM_TARGET_C_OBJECT,FPM_TARGET_CPP_OBJECT])) then - if (get_os_type()==OS_WINDOWS) then - targets(size(targets))%ptr%compile_flags = '/nofor-main' - else - targets(size(targets))%ptr%compile_flags = '-nofor-main' - end if - end if + associate(target => targets(size(targets))%ptr) + + select case (exe_type) + case (FPM_TARGET_C_OBJECT) + call model%compiler%get_main_flags("c",compile_flags) + case (FPM_TARGET_CPP_OBJECT) + call model%compiler%get_main_flags("c++",compile_flags) + case default + compile_flags = "" + end select + target%compile_flags = target%compile_flags//' '//compile_flags ! Executable depends on object - call add_dependency(targets(size(targets))%ptr, targets(size(targets)-1)%ptr) + call add_dependency(target, targets(size(targets)-1)%ptr) if (with_lib) then ! Executable depends on library - call add_dependency(targets(size(targets))%ptr, targets(1)%ptr) + call add_dependency(target, targets(1)%ptr) end if + endassociate + end select end do From 4bdf11781d02c172189376cf786ded117b5b3a2a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 30 Apr 2023 15:42:42 +0700 Subject: [PATCH 410/799] Use flag in build config --- src/fpm/manifest.f90 | 11 +----- src/fpm/manifest/build.f90 | 16 ++++++-- test/fpm_test/test_manifest.f90 | 67 +++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 70e0802efa..ae35cb3c8e 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -7,7 +7,6 @@ !> Additionally, the required data types for users of this module are reexported !> to hide the actual implementation details. module fpm_manifest - use fpm_manifest_build, only: build_config_t use fpm_manifest_example, only : example_config_t use fpm_manifest_executable, only : executable_config_t use fpm_manifest_dependency, only : dependency_config_t @@ -90,7 +89,7 @@ end subroutine default_test !> Obtain package meta data from a configuation file - subroutine get_package_data(package, file, error, apply_defaults, add_is_windows_macro) + subroutine get_package_data(package, file, error, apply_defaults) !> Parsed package meta data type(package_config_t), intent(out) :: package @@ -104,12 +103,8 @@ subroutine get_package_data(package, file, error, apply_defaults, add_is_windows !> Apply package defaults (uses file system operations) logical, intent(in), optional :: apply_defaults - !> Add `FPM_IS_WINDOWS` macro to the preprocessor - logical, intent(in), optional :: add_is_windows_macro - type(toml_table), allocatable :: table character(len=:), allocatable :: root - logical :: set_is_windows_macro call read_package_file(table, file, error) if (allocated(error)) return @@ -131,9 +126,7 @@ subroutine get_package_data(package, file, error, apply_defaults, add_is_windows end if end if - set_is_windows_macro = .true. - if (present(add_is_windows_macro)) set_is_windows_macro = add_is_windows_macro - if (set_is_windows_macro) call add_fpm_is_windows_macro(package%preprocess) + if (package%build%export_windows_macro) call add_fpm_is_windows_macro(package%preprocess) end subroutine get_package_data diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 8047dd045d..1e6ebc20bf 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -30,6 +30,9 @@ module fpm_manifest_build !> Automatic discovery of tests logical :: auto_tests + !> Export `FPM_IS_WINDOWS` macro on Windows. + logical :: export_windows_macro = .false. + !> Enforcing of package module names logical :: module_naming = .false. type(string_t) :: module_prefix @@ -89,6 +92,13 @@ subroutine new_build_config(self, table, error) return end if + call get_value(table, "export-windows-macro", self%export_windows_macro, .false., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'export-windows-macro' in fpm.toml, expecting logical") + return + end if + !> Module naming: fist, attempt boolean value first call get_value(table, "module-naming", self%module_naming, .false., stat=stat) @@ -147,10 +157,8 @@ subroutine check(table, error) do ikey = 1, size(list) select case(list(ikey)%key) - case("auto-executables", "auto-examples", "auto-tests", "link", "external-modules") - continue - - case ("module-naming") + case("auto-executables", "auto-examples", "auto-tests", "link", "external-modules", & + & "export-windows-macro", "module-naming") continue case default diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index b1e6eebb33..d2cbf9ccc8 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -70,6 +70,7 @@ subroutine collect_manifest(tests) & new_unittest("preprocessors-empty", test_preprocessors_empty, should_fail=.true.), & & new_unittest("macro-parsing", test_macro_parsing, should_fail=.false.), & & new_unittest("macro-parsing-dependency", test_macro_parsing_dependency, should_fail=.false.), & + & new_unittest("not-add-windows-macro", test_not_add_windows_macro), & & new_unittest("add-windows-macro-to-empty", test_add_windows_macro_to_empty), & & new_unittest("add-windows-macro-to-preprocess", test_add_windows_macro_to_preprocess), & & new_unittest("add-windows-macro-to-empty-macros", test_add_windows_macro_to_empty_macros), & @@ -96,6 +97,7 @@ subroutine test_valid_manifest(error) & '[build]', & & 'auto-executables = false', & & 'auto-tests = false', & + & 'export-windows-macro = false', & & 'module-naming = false', & & '[dependencies.fpm]', & & 'git = "https://github.com/fortran-lang/fpm"', & @@ -674,8 +676,9 @@ subroutine test_build_valid(error) & 'name = "example"', & & '[build]', & & 'auto-executables = false', & - & 'auto-tests = false ', & - & 'module-naming = true ' + & 'auto-tests = false', & + & 'export-windows-macro = true', & + & 'module-naming = true' close(unit) call get_package_data(package, temp_file, error) @@ -692,7 +695,12 @@ subroutine test_build_valid(error) return end if - if (.not.package%build%module_naming) then + if (.not. package%build%export_windows_macro) then + call test_failed(error, "Wong value of 'export-windows-macro' read, expecting .true.") + return + end if + + if (.not. package%build%module_naming) then call test_failed(error, "Wong value of 'module-naming' read, expecting .true.") return end if @@ -733,6 +741,11 @@ subroutine test_build_empty(error) return end if + if (package%build%export_windows_macro) then + call test_failed(error, "Wong default value of 'export-windows-macro' read, expecting .false.") + return + end if + if (package%build%module_naming) then call test_failed(error, "Wong default value of 'module-naming' read, expecting .false.") return @@ -1428,6 +1441,32 @@ subroutine test_macro_parsing_dependency(error) end subroutine test_macro_parsing_dependency + !> Not add `FPM_IS_WINDOWS` macro without flag. + subroutine test_not_add_windows_macro(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(package_config_t) :: package + character(:), allocatable :: temp_file + integer :: unit + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'name = "example"', & + & 'version = "0.1.0"' + close(unit) + + call get_package_data(package, temp_file, error) + open(file=temp_file, newunit=unit) + close(unit, status='delete') + if (allocated(error)) return + + if (allocated(package%preprocess)) call test_failed(error, 'Preprocess table should not be allocated.') + end + !> Add `FPM_IS_WINDOWS` macro when no preprocess table exists. subroutine test_add_windows_macro_to_empty(error) @@ -1443,10 +1482,14 @@ subroutine test_add_windows_macro_to_empty(error) open(file=temp_file, newunit=unit) write(unit, '(a)') & & 'name = "example"', & - & 'version = "0.1.0"' + & 'version = "0.1.0"', & + & '[build]', & + & 'export-windows-macro = true' close(unit) call get_package_data(package, temp_file, error) + open(file=temp_file, newunit=unit) + close(unit, status='delete') if (allocated(error)) return if (os_is_unix()) then @@ -1475,11 +1518,15 @@ subroutine test_add_windows_macro_to_preprocess(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & + & '[build]', & + & 'export-windows-macro = true', & & '[preprocess]', & & '[preprocess.cpp]' close(unit) call get_package_data(package, temp_file, error) + open(file=temp_file, newunit=unit) + close(unit, status='delete') if (allocated(error)) return if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') @@ -1510,12 +1557,16 @@ subroutine test_add_windows_macro_to_empty_macros(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & + & '[build]', & + & 'export-windows-macro = true', & & '[preprocess]', & & '[preprocess.cpp]', & & 'macros = []' close(unit) call get_package_data(package, temp_file, error) + open(file=temp_file, newunit=unit) + close(unit, status='delete') if (allocated(error)) return if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') @@ -1546,12 +1597,16 @@ subroutine test_add_windows_macro_to_other_macro(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & + & '[build]', & + & 'export-windows-macro = true', & & '[preprocess]', & & '[preprocess.cpp]', & & 'macros = ["ABC"]' close(unit) call get_package_data(package, temp_file, error) + open(file=temp_file, newunit=unit) + close(unit, status='delete') if (allocated(error)) return if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') @@ -1583,12 +1638,16 @@ subroutine test_add_second_windows_macro(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & + & '[build]', & + & 'export-windows-macro = true', & & '[preprocess]', & & '[preprocess.cpp]', & & 'macros = ["FPM_IS_WINDOWS"]' close(unit) call get_package_data(package, temp_file, error) + open(file=temp_file, newunit=unit) + close(unit, status='delete') if (allocated(error)) return if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') From 1c55f495e21dcce8412b0473c406f492b10bbb2e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 30 Apr 2023 15:46:46 +0700 Subject: [PATCH 411/799] Add flag to fpm manifest --- fpm.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fpm.toml b/fpm.toml index dcd3f27743..3846ad1106 100644 --- a/fpm.toml +++ b/fpm.toml @@ -5,6 +5,9 @@ author = "fpm maintainers" maintainer = "" copyright = "2020 fpm contributors" +[build] +export-windows-macro = true + [preprocess] [preprocess.cpp] macros=["FPM_RELEASE_VERSION={version}"] From 189dc8a5eef870f05db0f72f655eddac6720ce37 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 30 Apr 2023 16:53:41 +0700 Subject: [PATCH 412/799] Move to preprocessor table --- fpm.toml | 4 +- src/fpm/manifest.f90 | 42 +++++--------- src/fpm/manifest/build.f90 | 10 ---- src/fpm/manifest/preprocess.f90 | 28 +++++----- test/fpm_test/test_manifest.f90 | 99 +++++++++------------------------ 5 files changed, 55 insertions(+), 128 deletions(-) diff --git a/fpm.toml b/fpm.toml index 3846ad1106..9135415c43 100644 --- a/fpm.toml +++ b/fpm.toml @@ -5,11 +5,9 @@ author = "fpm maintainers" maintainer = "" copyright = "2020 fpm contributors" -[build] -export-windows-macro = true - [preprocess] [preprocess.cpp] +export-windows-macro = true macros=["FPM_RELEASE_VERSION={version}"] [dependencies] diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index ae35cb3c8e..2cf2ef4699 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -126,7 +126,7 @@ subroutine get_package_data(package, file, error, apply_defaults) end if end if - if (package%build%export_windows_macro) call add_fpm_is_windows_macro(package%preprocess) + call add_fpm_is_windows_macro(package%preprocess) end subroutine get_package_data @@ -193,33 +193,21 @@ subroutine add_fpm_is_windows_macro(preprocessors) integer :: i, j if (os_is_unix()) return - - if (allocated(preprocessors)) then - do i = 1, size(preprocessors) - if (preprocessors(i)%name == 'cpp') then - if (allocated(preprocessors(i)%macros)) then - ! Return if macro is already defined. - do j = 1, size(preprocessors(i)%macros) - if (preprocessors(i)%macros(j)%s == 'FPM_IS_WINDOWS') return - end do - ! Macro not found, therefore add it. - preprocessors(i)%macros = [preprocessors(i)%macros, string_t('FPM_IS_WINDOWS')] - else - preprocessors(i)%macros = [string_t('FPM_IS_WINDOWS')] - end if - return + if (.not. allocated(preprocessors)) return + do i = 1, size(preprocessors) + if (preprocessors(i)%export_windows_macro) then + if (allocated(preprocessors(i)%macros)) then + ! Do not add if macro is already defined. + do j = 1, size(preprocessors(i)%macros) + if (preprocessors(i)%macros(j)%s == 'FPM_IS_WINDOWS') cycle + end do + ! Macro not found, therefore add it. + preprocessors(i)%macros = [preprocessors(i)%macros, string_t('FPM_IS_WINDOWS')] + else + preprocessors(i)%macros = [string_t('FPM_IS_WINDOWS')] end if - end do - end if - - ! No cpp macros found, add one. - new_cpp%name = 'cpp' - new_cpp%macros = [string_t('FPM_IS_WINDOWS')] - if (allocated(preprocessors)) then - preprocessors = [preprocessors, new_cpp] - else - preprocessors = [new_cpp] - end if + end if + end do end diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 1e6ebc20bf..9daa300e86 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -30,9 +30,6 @@ module fpm_manifest_build !> Automatic discovery of tests logical :: auto_tests - !> Export `FPM_IS_WINDOWS` macro on Windows. - logical :: export_windows_macro = .false. - !> Enforcing of package module names logical :: module_naming = .false. type(string_t) :: module_prefix @@ -92,13 +89,6 @@ subroutine new_build_config(self, table, error) return end if - call get_value(table, "export-windows-macro", self%export_windows_macro, .false., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'export-windows-macro' in fpm.toml, expecting logical") - return - end if - !> Module naming: fist, attempt boolean value first call get_value(table, "module-naming", self%module_naming, .false., stat=stat) diff --git a/src/fpm/manifest/preprocess.f90 b/src/fpm/manifest/preprocess.f90 index 77e31cc2bd..ac7029945a 100644 --- a/src/fpm/manifest/preprocess.f90 +++ b/src/fpm/manifest/preprocess.f90 @@ -34,6 +34,9 @@ module fpm_manifest_preprocess !> Macros to be defined for the preprocessor type(string_t), allocatable :: macros(:) + !> Export `FPM_IS_WINDOWS` macro on Windows for the respective preprocessor. + logical :: export_windows_macro = .false. + contains !> Print information on this instance @@ -55,6 +58,8 @@ subroutine new_preprocess_config(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error + integer :: stat + call check(table, error) if (allocated(error)) return @@ -69,6 +74,11 @@ subroutine new_preprocess_config(self, table, error) call get_list(table, "macros", self%macros, error) if (allocated(error)) return + call get_value(table, "export-windows-macro", self%export_windows_macro, .false., stat=stat) + if (stat /= toml_stat%success) then + call syntax_error(error, "'export-windows-macro' must be a boolean."); return + end if + end subroutine new_preprocess_config !> Check local schema for allowed entries @@ -82,27 +92,17 @@ subroutine check(table, error) character(len=:), allocatable :: name type(toml_key), allocatable :: list(:) - logical :: suffixes_present, directories_present, macros_present integer :: ikey - suffixes_present = .false. - directories_present = .false. - macros_present = .false. - call table%get_key(name) call table%get_keys(list) do ikey = 1, size(list) select case(list(ikey)%key) - case default - call syntax_error(error, "Key " // list(ikey)%key // "is not allowed in preprocessor"//name) - exit - case("suffixes") - suffixes_present = .true. - case("directories") - directories_present = .true. - case("macros") - macros_present = .true. + !> Valid keys. + case("suffixes", "directories", "macros", "export-windows-macro") + case default + call syntax_error(error, "Key '"//list(ikey)%key//"' not allowed in preprocessor '"//name//"'."); exit end select end do end subroutine check diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index d2cbf9ccc8..6a45d3eddc 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -71,10 +71,9 @@ subroutine collect_manifest(tests) & new_unittest("macro-parsing", test_macro_parsing, should_fail=.false.), & & new_unittest("macro-parsing-dependency", test_macro_parsing_dependency, should_fail=.false.), & & new_unittest("not-add-windows-macro", test_not_add_windows_macro), & - & new_unittest("add-windows-macro-to-empty", test_add_windows_macro_to_empty), & - & new_unittest("add-windows-macro-to-preprocess", test_add_windows_macro_to_preprocess), & - & new_unittest("add-windows-macro-to-empty-macros", test_add_windows_macro_to_empty_macros), & - & new_unittest("add-windows-macro-to-other-macro", test_add_windows_macro_to_other_macro), & + & new_unittest("add-windows-macro-with-empty-macros", test_add_windows_macro_with_empty_macros), & + & new_unittest("add-windows-macro-to-fpp", test_add_windows_macro_to_fpp), & + & new_unittest("add-windows-macro-with-other-macro", test_add_windows_macro_with_other_macro), & & new_unittest("add-second-windows-macro", test_add_second_windows_macro) & & ] @@ -677,7 +676,6 @@ subroutine test_build_valid(error) & '[build]', & & 'auto-executables = false', & & 'auto-tests = false', & - & 'export-windows-macro = true', & & 'module-naming = true' close(unit) @@ -686,22 +684,17 @@ subroutine test_build_valid(error) if (allocated(error)) return if (package%build%auto_executables) then - call test_failed(error, "Wong value of 'auto-executables' read, expecting .false.") + call test_failed(error, "Wrong value of 'auto-executables' read, expecting .false.") return end if if (package%build%auto_tests) then - call test_failed(error, "Wong value of 'auto-tests' read, expecting .false.") - return - end if - - if (.not. package%build%export_windows_macro) then - call test_failed(error, "Wong value of 'export-windows-macro' read, expecting .true.") + call test_failed(error, "Wrong value of 'auto-tests' read, expecting .false.") return end if if (.not. package%build%module_naming) then - call test_failed(error, "Wong value of 'module-naming' read, expecting .true.") + call test_failed(error, "Wrong value of 'module-naming' read, expecting .true.") return end if @@ -732,22 +725,17 @@ subroutine test_build_empty(error) if (allocated(error)) return if (.not.package%build%auto_executables) then - call test_failed(error, "Wong default value of 'auto-executables' read, expecting .true.") + call test_failed(error, "Wrong default value of 'auto-executables' read, expecting .true.") return end if if (.not.package%build%auto_tests) then - call test_failed(error, "Wong default value of 'auto-tests' read, expecting .true.") - return - end if - - if (package%build%export_windows_macro) then - call test_failed(error, "Wong default value of 'export-windows-macro' read, expecting .false.") + call test_failed(error, "Wrong default value of 'auto-tests' read, expecting .true.") return end if if (package%build%module_naming) then - call test_failed(error, "Wong default value of 'module-naming' read, expecting .false.") + call test_failed(error, "Wrong default value of 'module-naming' read, expecting .false.") return end if @@ -1467,8 +1455,8 @@ subroutine test_not_add_windows_macro(error) if (allocated(package%preprocess)) call test_failed(error, 'Preprocess table should not be allocated.') end - !> Add `FPM_IS_WINDOWS` macro when no preprocess table exists. - subroutine test_add_windows_macro_to_empty(error) + !> Add `FPM_IS_WINDOWS` macro with empty macros table. + subroutine test_add_windows_macro_with_empty_macros(error) !> Error handling type(error_t), allocatable, intent(out) :: error @@ -1483,45 +1471,10 @@ subroutine test_add_windows_macro_to_empty(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & - & '[build]', & - & 'export-windows-macro = true' - close(unit) - - call get_package_data(package, temp_file, error) - open(file=temp_file, newunit=unit) - close(unit, status='delete') - if (allocated(error)) return - - if (os_is_unix()) then - if (allocated(package%preprocess)) call test_failed(error, 'Preprocess table should not be allocated.') - else - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, 'No cpp table created.') - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") - end if - end - - !> Add `FPM_IS_WINDOWS` macro to empty cpp table. - subroutine test_add_windows_macro_to_preprocess(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(package_config_t) :: package - character(:), allocatable :: temp_file - integer :: unit - - allocate(temp_file, source=get_temp_filename()) - - open(file=temp_file, newunit=unit) - write(unit, '(a)') & - & 'name = "example"', & - & 'version = "0.1.0"', & - & '[build]', & - & 'export-windows-macro = true', & & '[preprocess]', & - & '[preprocess.cpp]' + & '[preprocess.cpp]', & + & 'export-windows-macro = true', & + & 'macros = []' close(unit) call get_package_data(package, temp_file, error) @@ -1532,17 +1485,17 @@ subroutine test_add_windows_macro_to_preprocess(error) if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') if (os_is_unix()) then - if (allocated(package%preprocess(1)%macros)) call test_failed(error, "Macros should not be allocated.") + if (size(package%preprocess(1)%macros) /= 0) call test_failed(error, "Macros not empty.") else - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") end if end - !> Add `FPM_IS_WINDOWS` macro to empty macros table. - subroutine test_add_windows_macro_to_empty_macros(error) + !> Add `FPM_IS_WINDOWS` macro to an fpp table. + subroutine test_add_windows_macro_to_fpp(error) !> Error handling type(error_t), allocatable, intent(out) :: error @@ -1557,10 +1510,9 @@ subroutine test_add_windows_macro_to_empty_macros(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & - & '[build]', & - & 'export-windows-macro = true', & & '[preprocess]', & - & '[preprocess.cpp]', & + & '[preprocess.fpp]', & + & 'export-windows-macro = true', & & 'macros = []' close(unit) @@ -1571,7 +1523,8 @@ subroutine test_add_windows_macro_to_empty_macros(error) if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") + if (package%preprocess(1)%name == 'cpp') call test_failed(error, "cpp wasn't defined.") + if (package%preprocess(1)%name /= 'fpp') call test_failed(error, "Preprocessor isn't fpp.") if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') if (os_is_unix()) then @@ -1582,7 +1535,7 @@ subroutine test_add_windows_macro_to_empty_macros(error) end !> Add `FPM_IS_WINDOWS` macro to list of macros containing another macro. - subroutine test_add_windows_macro_to_other_macro(error) + subroutine test_add_windows_macro_with_other_macro(error) !> Error handling type(error_t), allocatable, intent(out) :: error @@ -1597,10 +1550,9 @@ subroutine test_add_windows_macro_to_other_macro(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & - & '[build]', & - & 'export-windows-macro = true', & & '[preprocess]', & & '[preprocess.cpp]', & + & 'export-windows-macro = true', & & 'macros = ["ABC"]' close(unit) @@ -1638,10 +1590,9 @@ subroutine test_add_second_windows_macro(error) write(unit, '(a)') & & 'name = "example"', & & 'version = "0.1.0"', & - & '[build]', & - & 'export-windows-macro = true', & & '[preprocess]', & & '[preprocess.cpp]', & + & 'export-windows-macro = true', & & 'macros = ["FPM_IS_WINDOWS"]' close(unit) From 75c19cb86a446e801d68d8db7a1e4f4edeaa1b61 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 30 Apr 2023 17:07:55 +0700 Subject: [PATCH 413/799] Remove remaining keys from build config --- src/fpm/manifest/build.f90 | 3 +-- test/fpm_test/test_manifest.f90 | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 9daa300e86..2a49c75e49 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -147,8 +147,7 @@ subroutine check(table, error) do ikey = 1, size(list) select case(list(ikey)%key) - case("auto-executables", "auto-examples", "auto-tests", "link", "external-modules", & - & "export-windows-macro", "module-naming") + case("auto-executables", "auto-examples", "auto-tests", "link", "external-modules", "module-naming") continue case default diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 6a45d3eddc..06de364340 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -96,7 +96,6 @@ subroutine test_valid_manifest(error) & '[build]', & & 'auto-executables = false', & & 'auto-tests = false', & - & 'export-windows-macro = false', & & 'module-naming = false', & & '[dependencies.fpm]', & & 'git = "https://github.com/fortran-lang/fpm"', & From 9b87b080f455ad8adb9371d5aea516644cc50a06 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 10:37:00 +0200 Subject: [PATCH 414/799] use Intel Classic action --- .github/workflows/meta.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c324cd0e44..714f34e97c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -90,11 +90,6 @@ jobs: compiler: intel-classic version: 2021.8 - - name: (Ubuntu) Install MPI SDK - if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') - run: DEBIAN_FRONTEND=noninteractive sudo apt-get install -y --no-install-recommends ca-certificates build-essential pkg-config gnupg libarchive13 openssh-server openssh-client wget net-tools git intel-basekit-getting-started intel-oneapi-advisor intel-oneapi-ccl-devel intel-oneapi-common-licensing intel-oneapi-common-vars intel-oneapi-compiler-dpcpp-cpp intel-oneapi-dal-devel intel-oneapi-dev-utilities intel-oneapi-dnnl-devel intel-oneapi-dpcpp-debugger intel-oneapi-ipp-devel intel-oneapi-ippcp-devel intel-oneapi-libdpstd-devel intel-oneapi-mkl-devel intel-oneapi-tbb-devel intel-oneapi-vtune intel-level-zero-gpu level-zero intel-hpckit-getting-started intel-oneapi-clck intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic intel-oneapi-compiler-fortran intel-oneapi-inspector intel-oneapi-itac intel-oneapi-mpi-devel - - - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 From a319e80553d40e5e089e5be39252e82c55ebaa79 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 10:53:00 +0200 Subject: [PATCH 415/799] use ifort compiler --- .github/workflows/meta.yml | 72 ++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 714f34e97c..7894519a90 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -27,44 +27,42 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11,windows-latest,ubuntu-latest] - mpi: [mpich,openmpi,msmpi,intel] - gcc_v: [10] # Version of GFortran we want to use - exclude: - - os: macos-11 - mpi: intel - - os: macos-11 - mpi: msmpi - - os: macos-11 # temporary - mpi: openmpi - - os: macos-11 # temporary - mpi: mpich - - os: windows-latest # temporary - mpi: msmpi - - os: windows-latest - mpi: mpich - - os: windows-latest - mpi: intel - - os: windows-latest - mpi: openmpi - - os: ubuntu-latest - mpi: openmpi - - os: ubuntu-latest - mpi: mpich - - os: ubuntu-latest - mpi: msmpi include: - - os: macos-11 - os-arch: macos-x86_64 - release-flags: --flag '-g -fbacktrace -O3' - - os: windows-latest - os-arch: windows-x86_64 - release-flags: --flag '--static -g -fbacktrace -O3' - exe: .exe - - env: - FC: gfortran - GCC_V: ${{ matrix.gcc_v }} + - os: ubuntu-latest + mpi: intel + env: + FC: ifort + FPM_FC: ifort + # os: [macos-11,windows-latest,ubuntu-latest] + #mpi: [mpich,openmpi,msmpi,intel] + #gcc_v: [10] # Version of GFortran we want to use + #exclude: + #- os: macos-11 + # mpi: intel + #- os: macos-11 + # mpi: msmpi + #- os: macos-11 # temporary + # mpi: openmpi + #- os: macos-11 # temporary + # mpi: mpich + #- os: windows-latest # temporary + # mpi: msmpi + #- os: windows-latest + # mpi: mpich + #- os: windows-latest + # mpi: intel + #- os: windows-latest + # mpi: openmpi + #- os: ubuntu-latest + # mpi: openmpi + #- os: ubuntu-latest + # mpi: mpich + #- os: ubuntu-latest + # mpi: msmpi + + # env: + # FC: gfortran + # GCC_V: ${{ matrix.gcc_v }} steps: - name: Checkout code From 557b5f7878718e8fd1d2cef9d1f590f0fab04158 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:02:26 +0200 Subject: [PATCH 416/799] export custom compiler via `FPM_FC` --- .github/workflows/meta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 7894519a90..f691a57ec8 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -227,5 +227,6 @@ jobs: - name: Run metapackage tests using the release version shell: bash run: | + FPM_FC=${{ env.FPM_FC }} ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From 0eb4f7f79df6befc2f1f116f209f2a1977c98f01 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:16:09 +0200 Subject: [PATCH 417/799] Update meta.yml --- .github/workflows/meta.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index f691a57ec8..3aa91adde6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -227,6 +227,7 @@ jobs: - name: Run metapackage tests using the release version shell: bash run: | - FPM_FC=${{ env.FPM_FC }} + echo "FPM_FC=${{ env.FPM_FC }}" >> $GITHUB_ENV + echo "using compiler $FPM_FC" ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From 049918505bd6e7ae00b65f07442329bf9cf5c75a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:27:05 +0200 Subject: [PATCH 418/799] Update meta.yml --- .github/workflows/meta.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 3aa91adde6..b132b200ce 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -30,9 +30,7 @@ jobs: include: - os: ubuntu-latest mpi: intel - env: - FC: ifort - FPM_FC: ifort + FPM_FC: ifort # os: [macos-11,windows-latest,ubuntu-latest] #mpi: [mpich,openmpi,msmpi,intel] #gcc_v: [10] # Version of GFortran we want to use @@ -227,7 +225,7 @@ jobs: - name: Run metapackage tests using the release version shell: bash run: | - echo "FPM_FC=${{ env.FPM_FC }}" >> $GITHUB_ENV + echo "FPM_FC=${{ matrix.FPM_FC }}" >> $GITHUB_ENV echo "using compiler $FPM_FC" ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From cd3dd0188ff7aec2599ea6a4f5dbcf7ad18078e9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:33:42 +0200 Subject: [PATCH 419/799] local env --- .github/workflows/meta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index b132b200ce..7cd905a35c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -226,6 +226,7 @@ jobs: shell: bash run: | echo "FPM_FC=${{ matrix.FPM_FC }}" >> $GITHUB_ENV + FPM_FC=${{ matrix.FPM_FC }} echo "using compiler $FPM_FC" ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From 9feec04b8b7e91022f46b53bee3461a1513e5140 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:37:28 +0200 Subject: [PATCH 420/799] Update meta.yml --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 7cd905a35c..2c3b92ff05 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -226,7 +226,7 @@ jobs: shell: bash run: | echo "FPM_FC=${{ matrix.FPM_FC }}" >> $GITHUB_ENV - FPM_FC=${{ matrix.FPM_FC }} + FPM_FC="${{ env.FPM_FC }}" echo "using compiler $FPM_FC" ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From c17c3ff58c0be406998dc72e068ec52b020bc37a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:43:19 +0200 Subject: [PATCH 421/799] Update meta.yml --- .github/workflows/meta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 2c3b92ff05..8795cbaec6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -226,7 +226,7 @@ jobs: shell: bash run: | echo "FPM_FC=${{ matrix.FPM_FC }}" >> $GITHUB_ENV - FPM_FC="${{ env.FPM_FC }}" - echo "using compiler $FPM_FC" + FPM_FC=$(echo ${{ env.FPM_FC }}) + echo "using compiler $FPM_FC " ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From a5ba91728415c050ff50905729c042bf2bf141d8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 11:51:29 +0200 Subject: [PATCH 422/799] Update meta.yml --- .github/workflows/meta.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 8795cbaec6..37eaa27c00 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -225,8 +225,7 @@ jobs: - name: Run metapackage tests using the release version shell: bash run: | - echo "FPM_FC=${{ matrix.FPM_FC }}" >> $GITHUB_ENV - FPM_FC=$(echo ${{ env.FPM_FC }}) + FPM_FC=$(echo ${{ matrix.FPM_FC }}) echo "using compiler $FPM_FC " ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From 0e24c344371bc57c9d7efef7180bbd596af11ddd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 12:03:12 +0200 Subject: [PATCH 423/799] use $FC --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 37eaa27c00..7326b362e7 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -225,7 +225,7 @@ jobs: - name: Run metapackage tests using the release version shell: bash run: | - FPM_FC=$(echo ${{ matrix.FPM_FC }}) + FPM_FC=$(echo ${{ env.FC }}) echo "using compiler $FPM_FC " ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" From 1269d72b71733aa65174a33d465c1446a46b0539 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 12:19:07 +0200 Subject: [PATCH 424/799] Update meta.yml --- .github/workflows/meta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 7326b362e7..b744d7e1c0 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -30,7 +30,6 @@ jobs: include: - os: ubuntu-latest mpi: intel - FPM_FC: ifort # os: [macos-11,windows-latest,ubuntu-latest] #mpi: [mpich,openmpi,msmpi,intel] #gcc_v: [10] # Version of GFortran we want to use @@ -225,7 +224,8 @@ jobs: - name: Run metapackage tests using the release version shell: bash run: | - FPM_FC=$(echo ${{ env.FC }}) echo "using compiler $FPM_FC " ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" + env: + FPM_FC : ${{ env.FC }} From 20a3416d47aed469fcfed92335c07e8cb3411671 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 12:49:23 +0200 Subject: [PATCH 425/799] export all intel vars --- .github/workflows/meta.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index b744d7e1c0..013c9f86fc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -85,6 +85,12 @@ jobs: compiler: intel-classic version: 2021.8 + - name: (Ubuntu) Setup Intel oneAPI environment + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') + run: | + source /opt/intel/oneapi/setvars.sh + printenv >> $GITHUB_ENV + - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') # there is not yet an environment variable for this path from msys2/setup-msys2 From b426f3430f6d82f8a4a412834a5340d3dd3a7454 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 07:33:06 -0500 Subject: [PATCH 426/799] IntelMPI: enable search via `I_MPI_ROOT` --- src/fpm_meta.f90 | 63 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 302ca0796a..e4807aeaed 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -95,7 +95,7 @@ module fpm_meta public :: MPI_TYPE_NAME !> Debugging information -logical, parameter, private :: verbose = .false. +logical, parameter, private :: verbose = .true. integer, parameter, private :: LANG_FORTRAN = 1 integer, parameter, private :: LANG_C = 2 @@ -474,6 +474,9 @@ subroutine init_mpi(this,compiler,error) cxxwrap = fort_wrappers(wcfit(LANG_FORTRAN)) end if + print *, 'wcfit = ',wcfit + print *, 'mpilib = ',mpilib + !> Initialize MPI package from wrapper command call init_mpi_from_wrappers(this,compiler,mpilib(LANG_FORTRAN),fwrap,cwrap,cxxwrap,error) if (allocated(error)) return @@ -499,15 +502,11 @@ subroutine wrapper_compiler_fit(fort_wrappers,c_wrappers,cpp_wrappers,compiler,w type(error_t), allocatable, intent(out) :: error integer, intent(out), dimension(3) :: wrap, mpi - logical :: has_wrappers type(error_t), allocatable :: wrap_error wrap = 0 mpi = MPI_TYPE_NONE - !> Were any wrappers found? - has_wrappers = size(fort_wrappers)*size(c_wrappers)*size(cpp_wrappers)>0 - if (size(fort_wrappers)>0) & call mpi_compiler_match(LANG_FORTRAN,fort_wrappers,compiler,wrap(LANG_FORTRAN),mpi(LANG_FORTRAN),wrap_error) @@ -1047,7 +1046,7 @@ subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) screen = mpi_wrapper_query(mpilib,wrappers(i),'compiler',verbose=.false.,error=error) if (allocated(error)) return - print *, 'screen <'//screen%s//'> compiler ',compiler%fc + print *, 'screen <'//screen%s//'> compiler ',compiler%fc,' language = ',language select case (language) @@ -1063,12 +1062,12 @@ subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) case (LANG_C) ! For other languages, we can only hope that the name matches the expected one - if (screen%s==compiler%cc) then + if (screen%s==compiler%cc .or. screen%s==compiler%fc) then which_one = i return end if case (LANG_CXX) - if (screen%s==compiler%cxx) then + if (screen%s==compiler%cxx .or. screen%s==compiler%fc) then which_one = i return end if @@ -1105,6 +1104,9 @@ subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) type(compiler_t), intent(in) :: compiler type(string_t), allocatable, intent(out) :: c_wrappers(:),cpp_wrappers(:),fort_wrappers(:) + character(len=:), allocatable :: mpi_root,intel_wrap + type(error_t), allocatable :: error + ! Attempt gathering MPI wrapper names from the environment variables c_wrappers = [string_t(get_env('MPICC' ,'mpicc'))] cpp_wrappers = [string_t(get_env('MPICXX','mpic++'))] @@ -1130,12 +1132,38 @@ subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) case (id_intel_classic_windows,id_intel_llvm_windows, & id_intel_classic_nix,id_intel_classic_mac,id_intel_llvm_nix,id_intel_llvm_unknown) - print *, 'intel wrappers' - c_wrappers = [string_t(get_env('I_MPI_CC','mpiicc'))] cpp_wrappers = [string_t(get_env('I_MPI_CXX','mpiicpc'))] fort_wrappers = [string_t(get_env('I_MPI_F90','mpiifort'))] + ! temporary + deallocate(c_wrappers,cpp_wrappers,fort_wrappers) + allocate(c_wrappers(0),cpp_wrappers(0),fort_wrappers(0)) + + ! It is possible that + mpi_root = get_env('I_MPI_ROOT') + + if (mpi_root/="") then + + mpi_root = join_path(mpi_root,'bin') + + print *, 'mpi_root',mpi_root + + intel_wrap = join_path(mpi_root,'mpiifort') + if (get_os_type()==OS_WINDOWS) intel_wrap = get_dos_path(intel_wrap,error) + if (intel_wrap/="") fort_wrappers = [fort_wrappers,string_t(intel_wrap)] + + intel_wrap = join_path(mpi_root,'mpiicc') + if (get_os_type()==OS_WINDOWS) intel_wrap = get_dos_path(intel_wrap,error) + if (intel_wrap/="") c_wrappers = [c_wrappers,string_t(intel_wrap)] + + intel_wrap = join_path(mpi_root,'mpiicpc') + if (get_os_type()==OS_WINDOWS) intel_wrap = get_dos_path(intel_wrap,error) + if (intel_wrap/="") cpp_wrappers = [cpp_wrappers,string_t(intel_wrap)] + + end if + + case (id_pgi,id_nvhpc) c_wrappers = [c_wrappers,string_t('mpipgicc'),string_t('mpgcc')] @@ -1209,6 +1237,15 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp end if end if + ! Empty command + if (len_trim(wrapper)<=0) then + if (verbose) print *, '+ ' + if (present(exitcode)) exitcode = 0 + if (present(cmd_success)) cmd_success = .true. + if (present(screen_output)) screen_output = string_t("") + return + end if + ! Init command command = trim(wrapper%s) @@ -1270,9 +1307,15 @@ integer function which_mpi_library(wrapper,compiler,verbose) ! Init as currently unsupported library which_mpi_library = MPI_TYPE_NONE + print *, 'len_trim= ',len_trim(wrapper) + + if (len_trim(wrapper)<=0) return + ! Run mpi wrapper first call run_mpi_wrapper(wrapper,verbose=verbose,cmd_success=is_mpi_wrapper) + print *, 'is_mpi_wrapper=',is_mpi_wrapper,' wrapper = ',wrapper%s + if (is_mpi_wrapper) then if (compiler%is_intel()) then From 1330d84b97301585204313124a16fe1d68344c88 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 07:42:49 -0500 Subject: [PATCH 427/799] use standard Intel compiler --- .github/workflows/meta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 013c9f86fc..c0a9897e6d 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -82,8 +82,8 @@ jobs: if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') uses: awvwgk/setup-fortran@v1 with: - compiler: intel-classic - version: 2021.8 + compiler: intel + version: 2023.0 - name: (Ubuntu) Setup Intel oneAPI environment if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') From 20d0b30f48e3489102860ec5f15314344c42bab6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 07:45:01 -0500 Subject: [PATCH 428/799] intel-classic 2023.0 --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c0a9897e6d..35f6dc2284 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -82,7 +82,7 @@ jobs: if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') uses: awvwgk/setup-fortran@v1 with: - compiler: intel + compiler: intel-classic version: 2023.0 - name: (Ubuntu) Setup Intel oneAPI environment From b4032642f11a9d798b4b9f084ce66fa67d89e05e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 07:48:05 -0500 Subject: [PATCH 429/799] 2022.0.2 --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 35f6dc2284..ffe3ea1b8d 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -83,7 +83,7 @@ jobs: uses: awvwgk/setup-fortran@v1 with: compiler: intel-classic - version: 2023.0 + version: 2022.0.2 - name: (Ubuntu) Setup Intel oneAPI environment if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') From 3d539e1b507563094af8c791e7a9204911776618 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 07:57:14 -0500 Subject: [PATCH 430/799] 2021.1.1 --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index ffe3ea1b8d..9c31cb40f3 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -83,7 +83,7 @@ jobs: uses: awvwgk/setup-fortran@v1 with: compiler: intel-classic - version: 2022.0.2 + version: 2021.1.1 - name: (Ubuntu) Setup Intel oneAPI environment if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') From ef57e4f4a20f588eea37b49af78b7a9ae10d29e6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 08:10:18 -0500 Subject: [PATCH 431/799] install mpi --- .github/workflows/meta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 9c31cb40f3..52eb636f3e 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -88,6 +88,7 @@ jobs: - name: (Ubuntu) Setup Intel oneAPI environment if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') run: | + apt-get install intel-oneapi-mpi-2021.1.1 intel-oneapi-mpi-2021.1.1 source /opt/intel/oneapi/setvars.sh printenv >> $GITHUB_ENV From eb02be505b4fa339ea29f1f277b4f813b4b71739 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 08:19:09 -0500 Subject: [PATCH 432/799] install mpi as sudo --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 52eb636f3e..040369870c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -88,7 +88,7 @@ jobs: - name: (Ubuntu) Setup Intel oneAPI environment if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') run: | - apt-get install intel-oneapi-mpi-2021.1.1 intel-oneapi-mpi-2021.1.1 + sudo apt-get install intel-oneapi-mpi-2021.1.1 intel-oneapi-mpi-2021.1.1 source /opt/intel/oneapi/setvars.sh printenv >> $GITHUB_ENV From 9c8ff2f8e5e31a18990a5581056a25819c62d7d3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 08:27:36 -0500 Subject: [PATCH 433/799] download latest api --- .github/workflows/meta.yml | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 040369870c..f046e8ad52 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -50,11 +50,11 @@ jobs: # mpi: intel #- os: windows-latest # mpi: openmpi - #- os: ubuntu-latest + #- os: ubuntu-latest # mpi: openmpi - #- os: ubuntu-latest + #- os: ubuntu-latest # mpi: mpich - #- os: ubuntu-latest + #- os: ubuntu-latest # mpi: msmpi # env: @@ -78,17 +78,24 @@ jobs: curl gcc-fortran - - name: (Ubuntu) Install Intel toolchain + - name: (Ubuntu) Retrieve Intel toolchain if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') - uses: awvwgk/setup-fortran@v1 - with: - compiler: intel-classic - version: 2021.1.1 + timeout-minutes: 1 + run: | + wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + rm GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list + sudo apt-get update + + - name: (Ubuntu) Install Intel oneAPI + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') + timeout-minutes: 5 + run: sudo apt-get install intel-oneapi-compiler-fortran intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic intel-oneapi-mpi intel-oneapi-mpi-devel intel-oneapi-mkl ninja-build - name: (Ubuntu) Setup Intel oneAPI environment if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') run: | - sudo apt-get install intel-oneapi-mpi-2021.1.1 intel-oneapi-mpi-2021.1.1 source /opt/intel/oneapi/setvars.sh printenv >> $GITHUB_ENV @@ -233,6 +240,6 @@ jobs: run: | echo "using compiler $FPM_FC " ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" - env: + env: FPM_FC : ${{ env.FC }} From ec2d800fab426cd1049c1c21b6503f48f1e7c0f2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 08:35:37 -0500 Subject: [PATCH 434/799] ifort: export FPM compiler flags --- .github/workflows/meta.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index f046e8ad52..c2839d0c03 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -98,6 +98,9 @@ jobs: run: | source /opt/intel/oneapi/setvars.sh printenv >> $GITHUB_ENV + echo "FPM_FC=ifort" >> $GITHUB_ENV + echo "FPM_CC=icc" >> $GITHUB_ENV + echo "FPM_CXX=icpc" >> $GITHUB_ENV - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') From 83bb67bc742412bc7f42e1c765a57c3277b1d284 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 08:57:19 -0500 Subject: [PATCH 435/799] ifort compiler fixes: test_manifest.f90 --- test/fpm_test/test_manifest.f90 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index ccb401b7c6..14f39991f7 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1333,7 +1333,7 @@ subroutine test_macro_parsing(error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package - character(:), allocatable :: temp_file + character(:), allocatable :: temp_file,pkg_ver integer :: unit integer(compiler_enum) :: id @@ -1352,7 +1352,9 @@ subroutine test_macro_parsing(error) if (allocated(error)) return - if (get_macros(id, package%preprocess(1)%macros, package%version%s()) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then + pkg_ver = package%version%s() + + if (get_macros(id, package%preprocess(1)%macros, pkg_ver) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then call test_failed(error, "Macros were not parsed correctly") end if @@ -1371,6 +1373,7 @@ subroutine test_macro_parsing_dependency(error) character(:), allocatable :: toml_file_package character(:), allocatable :: toml_file_dependency + character(:), allocatable :: pkg_ver,dep_ver integer :: unit integer(compiler_enum) :: id @@ -1407,8 +1410,11 @@ subroutine test_macro_parsing_dependency(error) if (allocated(error)) return - macrosPackage = get_macros(id, package%preprocess(1)%macros, package%version%s()) - macrosDependency = get_macros(id, dependency%preprocess(1)%macros, dependency%version%s()) + pkg_ver = package%version%s() + dep_ver = dependency%version%s() + + macrosPackage = get_macros(id, package%preprocess(1)%macros, pkg_ver) + macrosDependency = get_macros(id, dependency%preprocess(1)%macros, dep_ver) if (macrosPackage == macrosDependency) then call test_failed(error, "Macros of package and dependency should not be equal") From f7ee4fa90f28ee14a1951ade0980d6a025e03426 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 09:07:18 -0500 Subject: [PATCH 436/799] do not bootstrap FPM with Ifort --- .github/workflows/meta.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c2839d0c03..aa3aa6474a 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -98,9 +98,6 @@ jobs: run: | source /opt/intel/oneapi/setvars.sh printenv >> $GITHUB_ENV - echo "FPM_FC=ifort" >> $GITHUB_ENV - echo "FPM_CC=icc" >> $GITHUB_ENV - echo "FPM_CXX=icpc" >> $GITHUB_ENV - name: (Windows) Put MSYS2_MinGW64 on PATH if: contains(matrix.os,'windows') @@ -176,7 +173,7 @@ jobs: ${{ env.BOOTSTRAP }} run -- --version ${{ env.BOOTSTRAP }} run -- --help - - name: Test Fortran fpm (bootstrap) + - name: Test Fortran fpm (bootstrap) shell: bash run: | ${{ env.BOOTSTRAP }} test @@ -238,10 +235,17 @@ jobs: env: EXE: fpm-${{ env.VERSION }}-${{ matrix.os-arch }}${{ matrix.exe }} + - name: (Ubuntu) Use Intel compiler for the metapackage tests + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') + shell: bash + run: | + echo "FPM_FC=ifort" >> $GITHUB_ENV + echo "FPM_CC=icc" >> $GITHUB_ENV + echo "FPM_CXX=icpc" >> $GITHUB_ENV + - name: Run metapackage tests using the release version shell: bash run: | - echo "using compiler $FPM_FC " ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" env: FPM_FC : ${{ env.FC }} From 0ce5e0833fe8c9576c087dd6a0ecc7ddb6e1e49f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 09:13:14 -0500 Subject: [PATCH 437/799] do not override $FPM_FC --- .github/workflows/meta.yml | 2 -- src/fpm_meta.f90 | 10 +--------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index aa3aa6474a..688940c7e8 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -247,6 +247,4 @@ jobs: shell: bash run: | ci/meta_tests.sh "$PWD/${{ env.FPM_RELEASE }}" - env: - FPM_FC : ${{ env.FC }} diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e4807aeaed..f5e69f3ff0 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1136,19 +1136,12 @@ subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) cpp_wrappers = [string_t(get_env('I_MPI_CXX','mpiicpc'))] fort_wrappers = [string_t(get_env('I_MPI_F90','mpiifort'))] - ! temporary - deallocate(c_wrappers,cpp_wrappers,fort_wrappers) - allocate(c_wrappers(0),cpp_wrappers(0),fort_wrappers(0)) - - ! It is possible that + ! Also search MPI wrappers via the base MPI folder mpi_root = get_env('I_MPI_ROOT') - if (mpi_root/="") then mpi_root = join_path(mpi_root,'bin') - print *, 'mpi_root',mpi_root - intel_wrap = join_path(mpi_root,'mpiifort') if (get_os_type()==OS_WINDOWS) intel_wrap = get_dos_path(intel_wrap,error) if (intel_wrap/="") fort_wrappers = [fort_wrappers,string_t(intel_wrap)] @@ -1163,7 +1156,6 @@ subroutine mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) end if - case (id_pgi,id_nvhpc) c_wrappers = [c_wrappers,string_t('mpipgicc'),string_t('mpgcc')] From 0c584cb99e1b3ed0ce275bdbe8fa039a184f53ee Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 09:28:10 -0500 Subject: [PATCH 438/799] test ubuntu+openmpi --- .github/workflows/meta.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 688940c7e8..840f0a2b76 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -19,6 +19,7 @@ env: HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" HOMEBREW_NO_GITHUB_API: "ON" HOMEBREW_NO_INSTALL_CLEANUP: "ON" + GCC_V: "10" jobs: @@ -28,8 +29,10 @@ jobs: fail-fast: false matrix: include: + # - os: ubuntu-latest + #mpi: intel - os: ubuntu-latest - mpi: intel + mpi: openmpi # os: [macos-11,windows-latest,ubuntu-latest] #mpi: [mpich,openmpi,msmpi,intel] #gcc_v: [10] # Version of GFortran we want to use @@ -78,6 +81,19 @@ jobs: curl gcc-fortran + - name: (Ubuntu) Install gfortran + if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) + run: | + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \ + --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} + sudo apt-get update + + - name: (Ubuntu) Install OpenMPI + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'openmpi') + run: | + sudo apt install -y -q openmpi-bin libopenmpi-dev + - name: (Ubuntu) Retrieve Intel toolchain if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') timeout-minutes: 1 From c7e5a51cc031a1cb3abefd4752b92626ff779797 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 09:41:11 -0500 Subject: [PATCH 439/799] add verbosity --- ci/meta_tests.sh | 17 +++++++++++------ src/fpm_meta.f90 | 2 -- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ci/meta_tests.sh b/ci/meta_tests.sh index 16d333b530..54c70ce381 100755 --- a/ci/meta_tests.sh +++ b/ci/meta_tests.sh @@ -18,18 +18,23 @@ pushd example_packages/ rm -rf ./*/build pushd metapackage_openmp -"$fpm" build -"$fpm" run +"$fpm" build --verbose +"$fpm" run --verbose popd pushd metapackage_stdlib -"$fpm" build -"$fpm" run +"$fpm" build --verbose +"$fpm" run --verbose popd pushd metapackage_mpi -"$fpm" build -"$fpm" run +"$fpm" build --verbose +"$fpm" run --verbose +popd + +pushd metapackage_mpi_c +"$fpm" build --verbose +"$fpm" run --verbose popd # Cleanup diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index f5e69f3ff0..1c21610a45 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -796,8 +796,6 @@ subroutine get_mpi_runner(command,verbose,error) cycle end if - print *, 'command = ',command%s - ! Success! success = len_trim(command%s)>0 if (success) then From f739665f27674234167976760ccfaba0dc11777f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 09:49:44 -0500 Subject: [PATCH 440/799] request implicit typing --- src/fpm_meta.f90 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 1c21610a45..86d1c13d3a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -481,6 +481,13 @@ subroutine init_mpi(this,compiler,error) call init_mpi_from_wrappers(this,compiler,mpilib(LANG_FORTRAN),fwrap,cwrap,cxxwrap,error) if (allocated(error)) return + !> Request Fortran implicit typing + if (mpilib(LANG_FORTRAN)/=MPI_TYPE_INTEL) then + allocate(this%fortran) + this%fortran%implicit_typing = .true. + this%fortran%implicit_external = .true. + endif + end if 1 format('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0) @@ -649,7 +656,7 @@ logical function msmpi_init(this,compiler,error) result(found) end if use_prebuilt - !> Request no Fortran implicit typing + !> Request Fortran implicit typing allocate(this%fortran) this%fortran%implicit_typing = .true. this%fortran%implicit_external = .true. From 9c2bc681cf1853b2aaa01ece813deae4a6c30ed2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 09:57:29 -0500 Subject: [PATCH 441/799] Update meta.yml --- .github/workflows/meta.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 840f0a2b76..b9f6083d75 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -87,7 +87,6 @@ jobs: sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \ --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \ --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} - sudo apt-get update - name: (Ubuntu) Install OpenMPI if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'openmpi') From c39ef5a991993e9f131e397ec202590ba76143fa Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 10:07:25 -0500 Subject: [PATCH 442/799] add feature flags to the linker --- src/fpm_targets.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index d04b5859b5..5a01d34eb5 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -305,7 +305,8 @@ subroutine build_target_list(targets,model) call add_target(targets,package=model%packages(j)%name,type = FPM_TARGET_EXECUTABLE,& link_libraries = sources(i)%link_libraries, & output_name = join_path(exe_dir, & - sources(i)%exe_name//xsuffix)) + sources(i)%exe_name//xsuffix), & + features = model%packages(j)%features) ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option @@ -839,8 +840,9 @@ subroutine resolve_target_linking(targets, model) ! If the main program is a C/C++ one, Intel compilers require additional ! linking flag -nofor-main to avoid a "duplicate main" error, see ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main - if (model%compiler%is_intel() .and. target%target_type==FPM_TARGET_EXECUTABLE) then + if (target%target_type==FPM_TARGET_EXECUTABLE) then print *, 'target compile flags ',target%compile_flags + print *, 'target fortran features ',get_feature_flags(model%compiler, target%features) end if !> Get macros as flags. From a85c3bdb00d75f15837509e8dad61d6001aa50d1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 10:11:14 -0500 Subject: [PATCH 443/799] Revert "add feature flags to the linker" This reverts commit c39ef5a991993e9f131e397ec202590ba76143fa. --- src/fpm_targets.f90 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index 5a01d34eb5..d04b5859b5 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -305,8 +305,7 @@ subroutine build_target_list(targets,model) call add_target(targets,package=model%packages(j)%name,type = FPM_TARGET_EXECUTABLE,& link_libraries = sources(i)%link_libraries, & output_name = join_path(exe_dir, & - sources(i)%exe_name//xsuffix), & - features = model%packages(j)%features) + sources(i)%exe_name//xsuffix)) ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option @@ -840,9 +839,8 @@ subroutine resolve_target_linking(targets, model) ! If the main program is a C/C++ one, Intel compilers require additional ! linking flag -nofor-main to avoid a "duplicate main" error, see ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main - if (target%target_type==FPM_TARGET_EXECUTABLE) then + if (model%compiler%is_intel() .and. target%target_type==FPM_TARGET_EXECUTABLE) then print *, 'target compile flags ',target%compile_flags - print *, 'target fortran features ',get_feature_flags(model%compiler, target%features) end if !> Get macros as flags. From 8efb37554d8e9856e758f01504f9f9850e9f7eda Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 10:12:18 -0500 Subject: [PATCH 444/799] restore mac builds --- .github/workflows/meta.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index b9f6083d75..9b69069ecc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -29,10 +29,16 @@ jobs: fail-fast: false matrix: include: - # - os: ubuntu-latest - #mpi: intel - - os: ubuntu-latest - mpi: openmpi + - os: ubuntu-latest + mpi: intel + - os: macos-11 + mpi: openmpi + - os: macos-11 + mpi: mpich + #- os: windows-latest # temporary + # mpi: msmpi + # - os: ubuntu-latest DOES NOT WORK + #mpi: openmpi # os: [macos-11,windows-latest,ubuntu-latest] #mpi: [mpich,openmpi,msmpi,intel] #gcc_v: [10] # Version of GFortran we want to use @@ -41,12 +47,6 @@ jobs: # mpi: intel #- os: macos-11 # mpi: msmpi - #- os: macos-11 # temporary - # mpi: openmpi - #- os: macos-11 # temporary - # mpi: mpich - #- os: windows-latest # temporary - # mpi: msmpi #- os: windows-latest # mpi: mpich #- os: windows-latest From 82b992b8bfba8520a6545a25d54ee98e9fdf6db9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 12:54:00 -0500 Subject: [PATCH 445/799] GCC: do not care about linker order --- src/fpm_meta.f90 | 103 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 86d1c13d3a..cc7b6da9e4 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -970,12 +970,30 @@ subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cx ! Get linking flags if (mpilib/=MPI_TYPE_INTEL) then this%link_flags = mpi_wrapper_query(mpilib,fort_wrapper,'link',verbose,error) + + ! We fix OpenMPI's Fortran wrapper bug (https://github.com/open-mpi/ompi/issues/11636) + !call fix_openmpi_link_flags(this%link_flags,compiler,mpilib,fort_wrapper,c_wrapper,cxx_wrapper,error) + if (allocated(error)) return this%has_link_flags = len_trim(this%link_flags)>0 endif ! Add heading space - if (this%has_link_flags) this%link_flags = string_t(' '//this%link_flags%s) + if (this%has_link_flags) then + this%link_flags = string_t(' -Wl,--start-group '//this%link_flags%s) + +! ! If +! if (compiler%) then +! ! +! +! -Wl,--start-group +! +! +! end if + + + end if + ! Add language-specific flags call set_language_flags(mpilib,fort_wrapper,this%has_fortran_flags,this%fflags,verbose,error) @@ -1026,6 +1044,73 @@ end subroutine set_language_flags end subroutine init_mpi_from_wrappers +! Due to OpenMPI's Fortran wrapper bug (https://github.com/open-mpi/ompi/issues/11636) +! we need to check whether all library directories are real +subroutine check_openmpi_lib_dirs(link_flags,compiler,mpilib,fort_wrapper,c_wrapper,cxx_wrapper,error) + type(string_t), intent(inout) :: link_flags + type(compiler_t), intent(in) :: compiler + integer, intent(in) :: mpilib + type(string_t), intent(in) :: fort_wrapper,c_wrapper,cxx_wrapper + type(error_t), allocatable, intent(out) :: error + + character(:), allocatable :: tokens(:),dtokens(:),dir_name,cdir_name + type(string_t), allocatable :: include_dirs(:),dir_tokens(:) + type(string_t) :: new_dirs + integer :: i, j, k + integer, allocatable :: invalid_dirs(:) + + if (mpilib/=MPI_TYPE_OPENMPI .or. .not.os_is_unix()) return + if (len_trim(link_flags)<=0) return + + ! Extract library directory (-L/path/to/dir) from the linker flags + call split(link_flags%s,tokens,' ') + + allocate(invalid_dirs(0),include_dirs(0)) + check_lib_directories: do i=1,size(tokens) + if (str_begins_with_str(tokens(i),'-L')) then + dir_name = trim(tokens(i)(3:)) + if (.not.exists(join_path(dir_name,'.'))) then + invalid_dirs = [invalid_dirs,i] + print *, 'invalid directory: ',dir_name + endif + endif + end do check_lib_directories + + ! No invalid directories found + if (size(invalid_dirs)<=0) return + + ! The only viable strategy is to replace all invalid directory with all include directories. + ! Because include directories have Fortran .mod files and mpif.h, we hope the library files are there too. + ! Include directories need to be retrieved + if (size(invalid_dirs)>0 ) then + + ! Query include libraries for Fortran + new_dirs = mpi_wrapper_query(mpilib,fort_wrapper,'incl_dirs',verbose,error) + if (allocated(error) .or. len_trim(new_dirs)<=0) return + + ! Split into strings + call split(new_dirs%s,dtokens,' ') + allocate(dir_tokens(size(dtokens))) + do i=1,size(dtokens) + dir_tokens(i) = string_t('-L'//trim(adjustl(dtokens(i)))) + end do + new_dirs%s = string_cat(dir_tokens,' ') + + ! Assemble a unique token with the new library dirs + link_flags = string_t("") + do i=1,size(tokens) + if (i==invalid_dirs(1)) then + ! Replace invalid directory with the new library dirs + link_flags%s = link_flags%s//' '//trim(new_dirs%s) + else + link_flags%s = link_flags%s//' '//trim(tokens(i)) + end if + end do + + endif + +end subroutine check_openmpi_lib_dirs + !> Match one of the available compiler wrappers with the current compiler subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) integer, intent(in) :: language @@ -1384,10 +1469,6 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) return end if - ! Take out the first command from the whole line - call split(screen%s,tokens,delimiters=' ') - screen%s = trim(tokens(1)) - case (MPI_TYPE_INTEL) ! -show returns the build command of this wrapper @@ -1399,10 +1480,6 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) return end if - ! Take out the first command from the whole line - call split(screen%s,tokens,delimiters=' ') - screen%s = trim(tokens(1)) - print *, 'INTEL MPI compiler: ',screen%s case default @@ -1412,6 +1489,9 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) end select + ! Take out the first command from the whole line + call split(screen%s,tokens,delimiters=' ') + screen%s = trim(adjustl(tokens(1))) ! Get a list of additional compiler flags case ('flags') @@ -1486,6 +1566,9 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) call remove_new_lines(screen) + !> Address OpenMPI wrapper bug + + case (MPI_TYPE_MPICH) call run_mpi_wrapper(wrapper,[string_t('-link-info')],verbose=verbose, & @@ -1553,6 +1636,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) end select + call remove_new_lines(screen) + ! Retrieve library version case ('version') From 8b992dc289b09e0d236668dbefc87e8abcf7bc68 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 12:57:47 -0500 Subject: [PATCH 446/799] intel crash fix --- src/fpm_targets.f90 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index d04b5859b5..6feb1ef48a 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -308,25 +308,29 @@ subroutine build_target_list(targets,model) sources(i)%exe_name//xsuffix)) + associate(target=>targets(size(targets))%ptr) + ! If the main program is on a C/C++ source, the Intel Fortran compiler requires option ! -nofor-main to avoid "duplicate main" errors. ! https://stackoverflow.com/questions/36221612/p3dfft-compilation-ifort-compiler-error-multiple-definiton-of-main if (model%compiler%is_intel() .and. any(exe_type==[FPM_TARGET_C_OBJECT,FPM_TARGET_CPP_OBJECT])) then if (get_os_type()==OS_WINDOWS) then - targets(size(targets))%ptr%compile_flags = '/nofor-main' + target%compile_flags = '/nofor-main' else - targets(size(targets))%ptr%compile_flags = '-nofor-main' + target%compile_flags = '-nofor-main' end if end if ! Executable depends on object - call add_dependency(targets(size(targets))%ptr, targets(size(targets)-1)%ptr) + call add_dependency(target, targets(size(targets)-1)%ptr) if (with_lib) then ! Executable depends on library - call add_dependency(targets(size(targets))%ptr, targets(1)%ptr) + call add_dependency(target, targets(1)%ptr) end if + endassociate + end select end do From ee3fb61a1c1c3bfce9cd2eb97ab9c044c3c23d8a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 13:00:25 -0500 Subject: [PATCH 447/799] restore ubuntu openmpi --- .github/workflows/meta.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 9b69069ecc..f5c262196b 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -31,9 +31,11 @@ jobs: include: - os: ubuntu-latest mpi: intel - - os: macos-11 + - os: ubuntu-latest + mpi: openmpi + - os: macos-11 mpi: openmpi - - os: macos-11 + - os: macos-11 mpi: mpich #- os: windows-latest # temporary # mpi: msmpi @@ -81,12 +83,12 @@ jobs: curl gcc-fortran - - name: (Ubuntu) Install gfortran + - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) run: | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \ --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \ - --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} + --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V} - name: (Ubuntu) Install OpenMPI if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'openmpi') @@ -188,7 +190,7 @@ jobs: ${{ env.BOOTSTRAP }} run -- --version ${{ env.BOOTSTRAP }} run -- --help - - name: Test Fortran fpm (bootstrap) + - name: Test Fortran fpm (bootstrap) shell: bash run: | ${{ env.BOOTSTRAP }} test From 83f6a793b6c4f0b4bcf403e21967ba97e0306c23 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 13:03:28 -0500 Subject: [PATCH 448/799] C, C++ examples: add implicit features --- example_packages/metapackage_mpi_c/fpm.toml | 4 ++++ example_packages/metapackage_mpi_cpp/fpm.toml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/example_packages/metapackage_mpi_c/fpm.toml b/example_packages/metapackage_mpi_c/fpm.toml index 8fff9db364..ffbb88a139 100644 --- a/example_packages/metapackage_mpi_c/fpm.toml +++ b/example_packages/metapackage_mpi_c/fpm.toml @@ -9,6 +9,10 @@ copyright = "Copyright 2023, Federico Perini and the fpm maintainers" name = "test-mpi-c-main" main = "main.c" +[fortran] +implicit-typing=true +implicit-external=true + [dependencies] mpi = true diff --git a/example_packages/metapackage_mpi_cpp/fpm.toml b/example_packages/metapackage_mpi_cpp/fpm.toml index 7b2c39d386..5d236bcc89 100644 --- a/example_packages/metapackage_mpi_cpp/fpm.toml +++ b/example_packages/metapackage_mpi_cpp/fpm.toml @@ -9,6 +9,10 @@ copyright = "Copyright 2023, Federico Perini and the fpm maintainers" name = "test-mpi-cpp" main = "main.cpp" +[fortran] +implicit-typing=true +implicit-external=true + [dependencies] mpi = true From 812edce1c9a52852bb2d2ecc981cde24a6a222cf Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 20:35:56 +0200 Subject: [PATCH 449/799] ubuntu MPICH --- .github/workflows/meta.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index f5c262196b..5f55af21a7 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -33,9 +33,7 @@ jobs: mpi: intel - os: ubuntu-latest mpi: openmpi - - os: macos-11 - mpi: openmpi - - os: macos-11 + - os: ubuntu-latest mpi: mpich #- os: windows-latest # temporary # mpi: msmpi @@ -95,6 +93,11 @@ jobs: run: | sudo apt install -y -q openmpi-bin libopenmpi-dev + - name: (Ubuntu) Install MPICH + if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'mpich') + run: | + sudo apt install -y -q mpich + - name: (Ubuntu) Retrieve Intel toolchain if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') timeout-minutes: 1 From cf986e39eedf4e0a38b5f68896e2b2bc4c5fae2d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 21:03:41 +0200 Subject: [PATCH 450/799] print version line --- src/fpm_meta.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index cc7b6da9e4..8252964c47 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1184,6 +1184,8 @@ type(version_t) function mpi_version_get(mpilib,wrapper,error) version_line = mpi_wrapper_query(mpilib,wrapper,'version',error=error) if (allocated(error)) return + print *, 'version line = ',version_line%s + ! Wrap to object call new_version(mpi_version_get,version_line%s,error) @@ -1696,6 +1698,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) return else + print *, 'version line=',screen%s + ! Extract version ire = regex(screen%s,'\d+.\d+.\d+',length=length) if (ire>0 .and. length>0) then From caf0dc16d9da7a3009e37738592e72269c4767d1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 21:24:27 +0200 Subject: [PATCH 451/799] wrap version string extractor --- src/fpm_meta.f90 | 93 ++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 8252964c47..c3563e974f 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -836,6 +836,7 @@ subroutine compiler_get_version(self,version,is_msys2,error) type(error_t), allocatable, intent(out) :: error character(:), allocatable :: tmp_file,screen_output,line + type(string_t) :: ver integer :: stat,iunit,ire,length is_msys2 = .false. @@ -869,19 +870,11 @@ subroutine compiler_get_version(self,version,is_msys2,error) ! Check if this gcc is from the MSYS2 project is_msys2 = index(screen_output,'MSYS2')>0 - ! Extract version - ire = regex(screen_output,'\d+.\d+.\d+',length=length) - - if (ire>0 .and. length>0) then - ! Parse version into the object (this should always work) - screen_output = screen_output(ire:ire+length-1) - else - call syntax_error(error,'cannot retrieve '//self%fc//' compiler version.') - return - end if + ver = extract_version_text(screen_output,self%fc//' compiler',error) + if (allocated(error)) return - ! Wrap to object - call new_version(version,screen_output,error) + ! Extract version + call new_version(version,ver%s,error) case default @@ -1658,18 +1651,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) end if ! Extract version - ire = regex(screen%s,'\d+.\d+.\d+',length=length) - - if (ire>0 .and. length>0) then - - ! Parse version into the object (this should always work) - screen%s = screen%s(ire:ire+length-1) - - else - - call syntax_error(error,'cannot retrieve OpenMPI library version.') - - end if + screen = extract_version_text(screen%s,'OpenMPI library',error) + if (allocated(error)) return case (MPI_TYPE_MPICH) @@ -1698,16 +1681,8 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) return else - print *, 'version line=',screen%s - - ! Extract version - ire = regex(screen%s,'\d+.\d+.\d+',length=length) - if (ire>0 .and. length>0) then - ! Parse version into the object (this should always work) - screen%s = screen%s(ire:ire+length-1) - else - call syntax_error(error,'cannot retrieve MPICH library version.') - end if + screen = extract_version_text(screen%s,'MPICH library',error) + if (allocated(error)) return end if @@ -1724,23 +1699,9 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) call remove_new_lines(screen) end if - print *, 'version screen = ',screen%s - ! Extract version - ire = regex(screen%s,'\d+\.\d+\.\d+',length=length) - - print *, 'ire = ',ire,' length=',length - - if (ire>0 .and. length>0) then - - ! Parse version into the object (this should always work) - screen%s = screen%s(ire:ire+length-1) - - else - - call syntax_error(error,'cannot retrieve INTEL MPI library version.') - - end if + screen = extract_version_text(screen%s,'INTEL MPI library',error) + if (allocated(error)) return case default @@ -1800,4 +1761,36 @@ subroutine remove_new_lines(string) end subroutine remove_new_lines +type(string_t) function extract_version_text(text,what,error) result(ver) + character(*), intent(in) :: text + character(*), intent(in) :: what + type(error_t), allocatable, intent(out) :: error + + integer :: ire, length + + if (len_trim(text)<=0) then + call syntax_error(error,'cannot retrieve '//what//' version: empty input string') + return + end if + + ! Extract 3-sized version "1.0.4" + ire = regex(text,'\d+\.\d+\.\d+',length=length) + if (ire>0 .and. length>0) then + ! Parse version into the object (this should always work) + ver = string_t(text(ire:ire+length-1)) + else + + ! Try 2-sized version "1.0" + ire = regex(text,'\d+\.\d+',length=length) + + if (ire>0 .and. length>0) then + ver = string_t(text(ire:ire+length-1)) + else + call syntax_error(error,'cannot retrieve '//what//' version.') + end if + + end if + +end function extract_version_text + end module fpm_meta From 25ac71f30a5fab9ef42b5916902c2e7268ff07fb Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 21:31:46 +0200 Subject: [PATCH 452/799] fix linker library order for mac --- src/fpm_compiler.F90 | 7 +++++++ src/fpm_meta.f90 | 17 +++-------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 54b146a4ef..cc3e7d0f19 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -107,6 +107,8 @@ module fpm_compiler procedure :: is_unknown !> Check whether this is an Intel compiler procedure :: is_intel + !> Check whether this is a GNU compiler + procedure :: is_gnu !> Enumerate libraries, based on compiler and platform procedure :: enumerate_libraries !> Return compiler name @@ -899,6 +901,11 @@ pure logical function is_intel(self) id_intel_llvm_nix,id_intel_llvm_windows,id_intel_llvm_unknown]) end function is_intel +pure logical function is_gnu(self) + class(compiler_t), intent(in) :: self + is_gnu = any(self%id == [id_f95,id_gcc,id_caf]) +end function is_gnu + !> !> Enumerate libraries, based on compiler and platform !> diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c3563e974f..97bbb5c84a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -971,20 +971,9 @@ subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cx this%has_link_flags = len_trim(this%link_flags)>0 endif - ! Add heading space - if (this%has_link_flags) then - this%link_flags = string_t(' -Wl,--start-group '//this%link_flags%s) - -! ! If -! if (compiler%) then -! ! -! -! -Wl,--start-group -! -! -! end if - - + ! Request to use libs in arbitrary order + if (this%has_link_flags .and. compiler%is_gnu() .and. os_is_unix() .and. get_os_type()/=OS_MACOS) then + this%link_flags = string_t(' -Wl,--as-needed '//this%link_flags%s) end if From 59c5dbde7d3d1b56eb449400db0b7c7704ecf9c8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 21:38:46 +0200 Subject: [PATCH 453/799] start-group --- src/fpm_meta.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 97bbb5c84a..07a6c63e3d 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -973,7 +973,7 @@ subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cx ! Request to use libs in arbitrary order if (this%has_link_flags .and. compiler%is_gnu() .and. os_is_unix() .and. get_os_type()/=OS_MACOS) then - this%link_flags = string_t(' -Wl,--as-needed '//this%link_flags%s) + this%link_flags = string_t(' -Wl,--start-group '//this%link_flags%s) end if From 04a702482bc20f31d5525e81d406b5fad263e05b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 21:50:57 +0200 Subject: [PATCH 454/799] activate windows + msmpi --- .github/workflows/meta.yml | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 5f55af21a7..d88ffc2501 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -35,31 +35,8 @@ jobs: mpi: openmpi - os: ubuntu-latest mpi: mpich - #- os: windows-latest # temporary - # mpi: msmpi - # - os: ubuntu-latest DOES NOT WORK - #mpi: openmpi - # os: [macos-11,windows-latest,ubuntu-latest] - #mpi: [mpich,openmpi,msmpi,intel] - #gcc_v: [10] # Version of GFortran we want to use - #exclude: - #- os: macos-11 - # mpi: intel - #- os: macos-11 - # mpi: msmpi - #- os: windows-latest - # mpi: mpich - #- os: windows-latest - # mpi: intel - #- os: windows-latest - # mpi: openmpi - #- os: ubuntu-latest - # mpi: openmpi - #- os: ubuntu-latest - # mpi: mpich - #- os: ubuntu-latest - # mpi: msmpi - + - os: windows-latest + mpi: msmpi # env: # FC: gfortran # GCC_V: ${{ matrix.gcc_v }} From c958e37e22094fb94f732293d02b9d59d23a9897 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 22:11:30 +0200 Subject: [PATCH 455/799] cleanup debugging messages --- src/fpm_meta.f90 | 89 +++--------------------------------------------- 1 file changed, 5 insertions(+), 84 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 07a6c63e3d..84378a42d3 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -469,13 +469,14 @@ subroutine init_mpi(this,compiler,error) !> fortran compiler suite, we still want to enable C language flags as that is most likely being !> ABI-compatible anyways. However, issues may arise. !> see e.g. Homebrew with clabng C/C++ and GNU fortran at https://gitlab.kitware.com/cmake/cmake/-/issues/18139 - if (wcfit(LANG_FORTRAN)>0 .and. wcfit(LANG_C)==0 .and. wcfit(LANG_CXX)==0) then + if (wcfit(LANG_FORTRAN)>0 .and. all(wcfit([LANG_C,LANG_CXX])==0)) then cwrap = fort_wrappers(wcfit(LANG_FORTRAN)) cxxwrap = fort_wrappers(wcfit(LANG_FORTRAN)) end if - print *, 'wcfit = ',wcfit - print *, 'mpilib = ',mpilib + if (verbose) print *, '+ fortran MPI wrapper: ',fwrap%s + if (verbose) print *, '+ c MPI wrapper: ',cwrap%s + if (verbose) print *, '+ c++ MPI wrapper: ',cxxwrap%s !> Initialize MPI package from wrapper command call init_mpi_from_wrappers(this,compiler,mpilib(LANG_FORTRAN),fwrap,cwrap,cxxwrap,error) @@ -1018,7 +1019,7 @@ subroutine set_language_flags(mpilib,wrapper,has_flags,flags,verbose,error) ! Add heading space flags = string_t(' '//flags%s) - if (verbose) print *, 'MPI set language flags from wrapper <',wrapper%s,'>: flags=',flags%s + if (verbose) print *, '+ MPI language flags from wrapper <',wrapper%s,'>: flags=',flags%s endif @@ -1026,73 +1027,6 @@ end subroutine set_language_flags end subroutine init_mpi_from_wrappers -! Due to OpenMPI's Fortran wrapper bug (https://github.com/open-mpi/ompi/issues/11636) -! we need to check whether all library directories are real -subroutine check_openmpi_lib_dirs(link_flags,compiler,mpilib,fort_wrapper,c_wrapper,cxx_wrapper,error) - type(string_t), intent(inout) :: link_flags - type(compiler_t), intent(in) :: compiler - integer, intent(in) :: mpilib - type(string_t), intent(in) :: fort_wrapper,c_wrapper,cxx_wrapper - type(error_t), allocatable, intent(out) :: error - - character(:), allocatable :: tokens(:),dtokens(:),dir_name,cdir_name - type(string_t), allocatable :: include_dirs(:),dir_tokens(:) - type(string_t) :: new_dirs - integer :: i, j, k - integer, allocatable :: invalid_dirs(:) - - if (mpilib/=MPI_TYPE_OPENMPI .or. .not.os_is_unix()) return - if (len_trim(link_flags)<=0) return - - ! Extract library directory (-L/path/to/dir) from the linker flags - call split(link_flags%s,tokens,' ') - - allocate(invalid_dirs(0),include_dirs(0)) - check_lib_directories: do i=1,size(tokens) - if (str_begins_with_str(tokens(i),'-L')) then - dir_name = trim(tokens(i)(3:)) - if (.not.exists(join_path(dir_name,'.'))) then - invalid_dirs = [invalid_dirs,i] - print *, 'invalid directory: ',dir_name - endif - endif - end do check_lib_directories - - ! No invalid directories found - if (size(invalid_dirs)<=0) return - - ! The only viable strategy is to replace all invalid directory with all include directories. - ! Because include directories have Fortran .mod files and mpif.h, we hope the library files are there too. - ! Include directories need to be retrieved - if (size(invalid_dirs)>0 ) then - - ! Query include libraries for Fortran - new_dirs = mpi_wrapper_query(mpilib,fort_wrapper,'incl_dirs',verbose,error) - if (allocated(error) .or. len_trim(new_dirs)<=0) return - - ! Split into strings - call split(new_dirs%s,dtokens,' ') - allocate(dir_tokens(size(dtokens))) - do i=1,size(dtokens) - dir_tokens(i) = string_t('-L'//trim(adjustl(dtokens(i)))) - end do - new_dirs%s = string_cat(dir_tokens,' ') - - ! Assemble a unique token with the new library dirs - link_flags = string_t("") - do i=1,size(tokens) - if (i==invalid_dirs(1)) then - ! Replace invalid directory with the new library dirs - link_flags%s = link_flags%s//' '//trim(new_dirs%s) - else - link_flags%s = link_flags%s//' '//trim(tokens(i)) - end if - end do - - endif - -end subroutine check_openmpi_lib_dirs - !> Match one of the available compiler wrappers with the current compiler subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) integer, intent(in) :: language @@ -1111,16 +1045,11 @@ subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) do i=1,size(wrappers) - print *, 'TEST WRAPPER '//wrappers(i)%s - mpilib = which_mpi_library(wrappers(i),compiler,verbose=.false.) screen = mpi_wrapper_query(mpilib,wrappers(i),'compiler',verbose=.false.,error=error) if (allocated(error)) return - print *, 'screen <'//screen%s//'> compiler ',compiler%fc,' language = ',language - - select case (language) case (LANG_FORTRAN) ! Build compiler type. The ID is created based on the Fortran name @@ -1166,8 +1095,6 @@ type(version_t) function mpi_version_get(mpilib,wrapper,error) version_line = mpi_wrapper_query(mpilib,wrapper,'version',error=error) if (allocated(error)) return - print *, 'version line = ',version_line%s - ! Wrap to object call new_version(mpi_version_get,version_line%s,error) @@ -1373,15 +1300,11 @@ integer function which_mpi_library(wrapper,compiler,verbose) ! Init as currently unsupported library which_mpi_library = MPI_TYPE_NONE - print *, 'len_trim= ',len_trim(wrapper) - if (len_trim(wrapper)<=0) return ! Run mpi wrapper first call run_mpi_wrapper(wrapper,verbose=verbose,cmd_success=is_mpi_wrapper) - print *, 'is_mpi_wrapper=',is_mpi_wrapper,' wrapper = ',wrapper%s - if (is_mpi_wrapper) then if (compiler%is_intel()) then @@ -1464,8 +1387,6 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) return end if - print *, 'INTEL MPI compiler: ',screen%s - case default call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) From f724a26892ff28e480c07033563b2b535ecaca82 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 22:23:09 +0200 Subject: [PATCH 456/799] test msmpi --- src/fpm_meta.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 84378a42d3..9466dad6a8 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -573,6 +573,8 @@ logical function msmpi_init(this,compiler,error) result(found) ! Check that the runtime is installed bindir = get_env('MSMPI_BIN') + print *, 'bindir=',bindir + ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). ! Do a second attempt: search for mpiexec.exe if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then @@ -715,6 +717,8 @@ subroutine find_command_location(command,path,echo,verbose,error) return end if + print *, 'searching '//command + tmp_file = get_temp_filename() if (get_os_type()==OS_WINDOWS) then @@ -723,7 +727,7 @@ subroutine find_command_location(command,path,echo,verbose,error) call run("which "//command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) end if if (stat/=0) then - call fatal_error(error,'compiler_get_path failed for '//command) + call fatal_error(error,'find_command_location failed for '//command) return end if From c608c96e118fdcf63ce280b3e7c32267d5e8c43b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 22:37:14 +0200 Subject: [PATCH 457/799] cleanup --- src/fpm_meta.f90 | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 9466dad6a8..aee1561425 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -474,9 +474,9 @@ subroutine init_mpi(this,compiler,error) cxxwrap = fort_wrappers(wcfit(LANG_FORTRAN)) end if - if (verbose) print *, '+ fortran MPI wrapper: ',fwrap%s - if (verbose) print *, '+ c MPI wrapper: ',cwrap%s - if (verbose) print *, '+ c++ MPI wrapper: ',cxxwrap%s + if (verbose) print *, '+ MPI fortran wrapper: ',fwrap%s + if (verbose) print *, '+ MPI c wrapper: ',cwrap%s + if (verbose) print *, '+ MPI c++ wrapper: ',cxxwrap%s !> Initialize MPI package from wrapper command call init_mpi_from_wrappers(this,compiler,mpilib(LANG_FORTRAN),fwrap,cwrap,cxxwrap,error) @@ -709,7 +709,7 @@ subroutine find_command_location(command,path,echo,verbose,error) logical, optional, intent(in) :: echo,verbose type(error_t), allocatable, intent(out) :: error - character(:), allocatable :: tmp_file,screen_output,line,fullpath + character(:), allocatable :: tmp_file,screen_output,line,fullpath,search_command integer :: stat,iunit,ire,length if (len_trim(command)<=0) then @@ -717,15 +717,11 @@ subroutine find_command_location(command,path,echo,verbose,error) return end if - print *, 'searching '//command - tmp_file = get_temp_filename() - if (get_os_type()==OS_WINDOWS) then - call run("where "//command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) - else - call run("which "//command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) - end if + search_command = merge("where ","which ",get_os_type()==OS_WINDOWS)//command + + call run(search_command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) if (stat/=0) then call fatal_error(error,'find_command_location failed for '//command) return @@ -763,11 +759,8 @@ subroutine find_command_location(command,path,echo,verbose,error) return end if - print *, 'fullpath <'//fullpath//'>, command=<'//command//'>' - ! Extract path only length = index(fullpath,command,BACK=.true.) - print *, 'length=',length if (length<=0) then call fatal_error(error,'full path to command ('//command//') does not include command name') return @@ -802,11 +795,7 @@ subroutine get_mpi_runner(command,verbose,error) ! Try several commands do itri=1,size(try) call find_command_location(trim(try(itri)),command%s,verbose=.true.,error=error) - - if (allocated(error)) then - print *, 'error returned: ',error%message - cycle - end if + if (allocated(error)) cycle ! Success! success = len_trim(command%s)>0 @@ -814,7 +803,6 @@ subroutine get_mpi_runner(command,verbose,error) command%s = join_path(command%s,trim(try(itri))) return endif - end do ! No valid command found @@ -1193,7 +1181,7 @@ subroutine assert_mpi_wrappers(wrappers,compiler,verbose) allocate(works(size(wrappers))) do i=1,size(wrappers) - print *, 'test wrapper <', wrappers(i)%s,'>' + if (verbose) print *, '+ MPI test wrapper <',wrappers(i)%s,'>' works(i) = which_mpi_library(wrappers(i),compiler,verbose) end do From 4ca8e8df679427e82c10e616337034185a9e99dd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 22:41:50 +0200 Subject: [PATCH 458/799] try both which, where on WSL --- src/fpm_meta.f90 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index aee1561425..2833bc46fe 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -710,7 +710,8 @@ subroutine find_command_location(command,path,echo,verbose,error) type(error_t), allocatable, intent(out) :: error character(:), allocatable :: tmp_file,screen_output,line,fullpath,search_command - integer :: stat,iunit,ire,length + integer :: stat,iunit,ire,length,try + character(*), parameter :: search(2) = ["where ","which "] if (len_trim(command)<=0) then call fatal_error(error,'empty command provided in find_command_location') @@ -719,9 +720,12 @@ subroutine find_command_location(command,path,echo,verbose,error) tmp_file = get_temp_filename() - search_command = merge("where ","which ",get_os_type()==OS_WINDOWS)//command - - call run(search_command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) + ! On Windows, we try both commands because we may be on WSL + do try=merge(1,2,get_os_type()==OS_WINDOWS),2 + search_command = search(try)//command + call run(search_command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) + if (stat==0) exit + end do if (stat/=0) then call fatal_error(error,'find_command_location failed for '//command) return From 0ff6be9e70872bf15a4f3743dc0f025429642537 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 2 May 2023 22:45:02 +0200 Subject: [PATCH 459/799] fix verbose --- src/fpm_meta.f90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 2833bc46fe..2c26ea6a78 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1185,7 +1185,9 @@ subroutine assert_mpi_wrappers(wrappers,compiler,verbose) allocate(works(size(wrappers))) do i=1,size(wrappers) - if (verbose) print *, '+ MPI test wrapper <',wrappers(i)%s,'>' + if (present(verbose)) then + if (verbose) print *, '+ MPI test wrapper <',wrappers(i)%s,'>' + endif works(i) = which_mpi_library(wrappers(i),compiler,verbose) end do From 3feaae3d2f6b4790c57362068bcc93518dbe1d41 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:27:55 +0200 Subject: [PATCH 460/799] cleanup MPI wrapper function --- src/fpm_meta.f90 | 186 ++++++++++++++++------------------------------- 1 file changed, 61 insertions(+), 125 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 2c26ea6a78..bf3efcf54f 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1248,7 +1248,6 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp endif add_arguments if (echo_local) print *, '+ ', command - print *, '+ ', command ! Test command call execute_command_line(command//redirect_str,exitstat=stat,cmdstat=cmdstat) @@ -1341,57 +1340,36 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) type(error_t), allocatable, intent(out) :: error logical :: success - character(:), allocatable :: redirect_str,tokens(:) + character(:), allocatable :: redirect_str,tokens(:),unsupported_msg type(string_t) :: cmdstr type(compiler_t) :: mpi_compiler integer :: stat,cmdstat,ire,length + unsupported_msg = 'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command) + select case (command) ! Get MPI compiler name case ('compiler') select case (mpilib) - case (MPI_TYPE_OPENMPI) - - ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:command')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) - - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local OpenMPI library does not support --showme:command') - return - end if - - case (MPI_TYPE_MPICH) - - ! -compile_info returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('-compile-info')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) - - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local MPICH library does not support -compile-info') - return - end if - - case (MPI_TYPE_INTEL) - - ! -show returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('-show')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) - - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local INTEL MPI library does not support -show') - return - end if - + case (MPI_TYPE_OPENMPI); cmdstr = string_t('--showme:command') + case (MPI_TYPE_MPICH); cmdstr = string_t('-compile-info') + case (MPI_TYPE_INTEL); cmdstr = string_t('-show') case default - - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,unsupported_msg) return - end select + call run_mpi_wrapper(wrapper,[cmdstr],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) + + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local '//MPI_TYPE_NAME(mpilib)//& + ' library wrapper does not support flag '//cmdstr%s) + return + end if + ! Take out the first command from the whole line call split(screen%s,tokens,delimiters=' ') screen%s = trim(adjustl(tokens(1))) @@ -1400,99 +1378,71 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) case ('flags') select case (mpilib) - case (MPI_TYPE_OPENMPI) - - ! --showme:command returns the build command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:compile')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) - - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local OpenMPI library does not support --showme:compile') - return - end if - - call remove_new_lines(screen) - - case (MPI_TYPE_MPICH) + case (MPI_TYPE_OPENMPI); cmdstr = string_t('--showme:compile') + case (MPI_TYPE_MPICH); cmdstr = string_t('-compile-info') + case (MPI_TYPE_INTEL); cmdstr = string_t('-show') + case default + call fatal_error(error,unsupported_msg) + return + end select - call run_mpi_wrapper(wrapper,[string_t('-compile-info')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) + call run_mpi_wrapper(wrapper,[cmdstr],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local MPICH library does not support -compile-info') - return - end if + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local '//MPI_TYPE_NAME(mpilib)//& + ' library wrapper does not support flag '//cmdstr%s) + return + end if - ! MPICH reports the full command including the compiler name. Remove it if so + ! Post-process output + select case (mpilib) + case (MPI_TYPE_OPENMPI) + ! This library reports the compiler name only call remove_new_lines(screen) - call split(screen%s,tokens) - ! Remove trailing compiler name - screen%s = screen%s(len_trim(tokens(1))+1:) - - case (MPI_TYPE_INTEL) - - call run_mpi_wrapper(wrapper,[string_t('-show')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) - - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local INTEL MPI library does not support -show') - return - end if - - ! MPICH reports the full command including the compiler name. Remove it if so + case (MPI_TYPE_MPICH,MPI_TYPE_INTEL) + ! These libraries report the full command including the compiler name. Remove it if so call remove_new_lines(screen) call split(screen%s,tokens) ! Remove trailing compiler name screen%s = screen%s(len_trim(tokens(1))+1:) - case default - - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,'invalid MPI library type') return - end select ! Get a list of additional linker flags case ('link') select case (mpilib) - case (MPI_TYPE_OPENMPI) + case (MPI_TYPE_OPENMPI); cmdstr = string_t('--showme:link') + case (MPI_TYPE_MPICH); cmdstr = string_t('-link-info') + case default + call fatal_error(error,unsupported_msg) + return + end select - ! --showme:link returns the linker command of this wrapper - call run_mpi_wrapper(wrapper,[string_t('--showme:link')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) + call run_mpi_wrapper(wrapper,[cmdstr],verbose=verbose, & + exitcode=stat,cmd_success=success,screen_output=screen) - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local OpenMPI library does not support --showme:link') - return - end if + if (stat/=0 .or. .not.success) then + call syntax_error(error,'local '//MPI_TYPE_NAME(mpilib)//& + ' library wrapper does not support flag '//cmdstr%s) + return + end if + select case (mpilib) + case (MPI_TYPE_OPENMPI) call remove_new_lines(screen) - - !> Address OpenMPI wrapper bug - - case (MPI_TYPE_MPICH) - - call run_mpi_wrapper(wrapper,[string_t('-link-info')],verbose=verbose, & - exitcode=stat,cmd_success=success,screen_output=screen) - - if (stat/=0 .or. .not.success) then - call syntax_error(error,'local MPICH library does not support -link-info') - return - end if - ! MPICH reports the full command including the compiler name. Remove it if so call remove_new_lines(screen) call split(screen%s,tokens) ! Remove trailing compiler name screen%s = screen%s(len_trim(tokens(1))+1:) - case default - - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,unsupported_msg) return - end select ! Get a list of MPI library directories @@ -1512,7 +1462,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,unsupported_msg) return end select @@ -1522,21 +1472,16 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) select case (mpilib) case (MPI_TYPE_OPENMPI) - ! --showme:command returns the build command of this wrapper call run_mpi_wrapper(wrapper,[string_t('--showme:incdirs')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) - if (stat/=0 .or. .not.success) then call syntax_error(error,'local OpenMPI library does not support --showme:incdirs') return end if - case default - - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,unsupported_msg) return - end select call remove_new_lines(screen) @@ -1558,10 +1503,6 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) call remove_new_lines(screen) end if - ! Extract version - screen = extract_version_text(screen%s,'OpenMPI library',error) - if (allocated(error)) return - case (MPI_TYPE_MPICH) !> MPICH offers command "mpichversion" in the same system folder as the MPI wrappers. @@ -1587,11 +1528,6 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) if (stat/=0 .or. .not.success) then call syntax_error(error,'cannot retrieve MPICH library version from ') return - else - - screen = extract_version_text(screen%s,'MPICH library',error) - if (allocated(error)) return - end if case (MPI_TYPE_INTEL) @@ -1607,17 +1543,17 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) call remove_new_lines(screen) end if - ! Extract version - screen = extract_version_text(screen%s,'INTEL MPI library',error) - if (allocated(error)) return - case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,unsupported_msg) return end select + ! Extract version + screen = extract_version_text(screen%s,MPI_TYPE_NAME(mpilib)//' library',error) + if (allocated(error)) return + ! Get path to the MPI runner command case ('runner') @@ -1625,7 +1561,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) case (MPI_TYPE_OPENMPI,MPI_TYPE_MPICH,MPI_TYPE_MSMPI,MPI_TYPE_INTEL) call get_mpi_runner(screen,verbose,error) case default - call fatal_error(error,'the MPI library of wrapper '//wrapper%s//' does not support task '//trim(command)) + call fatal_error(error,unsupported_msg) return end select From 08ed7e69c3206a170e7f09f7d1d6d54c760b5ed7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:30:36 +0200 Subject: [PATCH 461/799] regex version: move to `fpm_versioning` --- src/fpm/versioning.f90 | 34 ++++++++++++++++++++++++++++++++++ src/fpm_meta.f90 | 38 +++----------------------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/fpm/versioning.f90 b/src/fpm/versioning.f90 index 4c7c01712a..b1da46a960 100644 --- a/src/fpm/versioning.f90 +++ b/src/fpm/versioning.f90 @@ -5,6 +5,7 @@ module fpm_versioning private public :: version_t, new_version + public :: regex_version_from_text type :: version_t @@ -390,5 +391,38 @@ elemental function match(lhs, rhs) end function match + ! Extract canonical version flags "1.0.0" or "1.0" as the first instance inside a text + ! (whatever long) using regex + type(string_t) function regex_version_from_text(text,what,error) result(ver) + character(*), intent(in) :: text + character(*), intent(in) :: what + type(error_t), allocatable, intent(out) :: error + + integer :: ire, length + + if (len_trim(text)<=0) then + call syntax_error(error,'cannot retrieve '//what//' version: empty input string') + return + end if + + ! Extract 3-sized version "1.0.4" + ire = regex(text,'\d+\.\d+\.\d+',length=length) + if (ire>0 .and. length>0) then + ! Parse version into the object (this should always work) + ver = string_t(text(ire:ire+length-1)) + else + + ! Try 2-sized version "1.0" + ire = regex(text,'\d+\.\d+',length=length) + + if (ire>0 .and. length>0) then + ver = string_t(text(ire:ire+length-1)) + else + call syntax_error(error,'cannot retrieve '//what//' version.') + end if + + end if + + end function regex_version_from_text end module fpm_versioning diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index bf3efcf54f..c46b75853b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -23,7 +23,7 @@ module fpm_meta use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix use fpm_filesystem, only: run, get_temp_filename, getline, exists, canon_path, is_dir -use fpm_versioning, only: version_t, new_version +use fpm_versioning, only: version_t, new_version, regex_version_from_text use fpm_os, only: get_absolute_path use iso_fortran_env, only: stdout => output_unit use regex_module, only: regex @@ -867,7 +867,7 @@ subroutine compiler_get_version(self,version,is_msys2,error) ! Check if this gcc is from the MSYS2 project is_msys2 = index(screen_output,'MSYS2')>0 - ver = extract_version_text(screen_output,self%fc//' compiler',error) + ver = regex_version_from_text(screen_output,self%fc//' compiler',error) if (allocated(error)) return ! Extract version @@ -1551,7 +1551,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) end select ! Extract version - screen = extract_version_text(screen%s,MPI_TYPE_NAME(mpilib)//' library',error) + screen = regex_version_from_text(screen%s,MPI_TYPE_NAME(mpilib)//' library',error) if (allocated(error)) return ! Get path to the MPI runner command @@ -1605,36 +1605,4 @@ subroutine remove_new_lines(string) end subroutine remove_new_lines -type(string_t) function extract_version_text(text,what,error) result(ver) - character(*), intent(in) :: text - character(*), intent(in) :: what - type(error_t), allocatable, intent(out) :: error - - integer :: ire, length - - if (len_trim(text)<=0) then - call syntax_error(error,'cannot retrieve '//what//' version: empty input string') - return - end if - - ! Extract 3-sized version "1.0.4" - ire = regex(text,'\d+\.\d+\.\d+',length=length) - if (ire>0 .and. length>0) then - ! Parse version into the object (this should always work) - ver = string_t(text(ire:ire+length-1)) - else - - ! Try 2-sized version "1.0" - ire = regex(text,'\d+\.\d+',length=length) - - if (ire>0 .and. length>0) then - ver = string_t(text(ire:ire+length-1)) - else - call syntax_error(error,'cannot retrieve '//what//' version.') - end if - - end if - -end function extract_version_text - end module fpm_meta From cd2a02f3fe0ff5f394957ebc0991c188deb09de4 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:38:02 +0200 Subject: [PATCH 462/799] remove_newline_characters: move to `fpm_strings` --- src/fpm/versioning.f90 | 2 ++ src/fpm_meta.f90 | 50 ++++++++---------------------------------- src/fpm_strings.f90 | 42 ++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/src/fpm/versioning.f90 b/src/fpm/versioning.f90 index b1da46a960..d1e130a2c3 100644 --- a/src/fpm/versioning.f90 +++ b/src/fpm/versioning.f90 @@ -1,6 +1,8 @@ !> Implementation of versioning data for comparing packages module fpm_versioning use fpm_error, only : error_t, syntax_error + use fpm_strings, only: string_t + use regex_module, only: regex implicit none private diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c46b75853b..e78fe13181 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -13,7 +13,7 @@ !> !> module fpm_meta -use fpm_strings, only: string_t, len_trim +use fpm_strings, only: string_t, len_trim, remove_newline_characters use fpm_error, only: error_t, fatal_error, syntax_error, fpm_stop use fpm_compiler use fpm_model @@ -26,7 +26,6 @@ module fpm_meta use fpm_versioning, only: version_t, new_version, regex_version_from_text use fpm_os, only: get_absolute_path use iso_fortran_env, only: stdout => output_unit -use regex_module, only: regex implicit none @@ -1399,10 +1398,10 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) select case (mpilib) case (MPI_TYPE_OPENMPI) ! This library reports the compiler name only - call remove_new_lines(screen) + call remove_newline_characters(screen) case (MPI_TYPE_MPICH,MPI_TYPE_INTEL) ! These libraries report the full command including the compiler name. Remove it if so - call remove_new_lines(screen) + call remove_newline_characters(screen) call split(screen%s,tokens) ! Remove trailing compiler name screen%s = screen%s(len_trim(tokens(1))+1:) @@ -1433,10 +1432,10 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) select case (mpilib) case (MPI_TYPE_OPENMPI) - call remove_new_lines(screen) + call remove_newline_characters(screen) case (MPI_TYPE_MPICH) ! MPICH reports the full command including the compiler name. Remove it if so - call remove_new_lines(screen) + call remove_newline_characters(screen) call split(screen%s,tokens) ! Remove trailing compiler name screen%s = screen%s(len_trim(tokens(1))+1:) @@ -1484,7 +1483,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) return end select - call remove_new_lines(screen) + call remove_newline_characters(screen) ! Retrieve library version case ('version') @@ -1500,7 +1499,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) call syntax_error(error,'local OpenMPI library does not support --showme:version') return else - call remove_new_lines(screen) + call remove_newline_characters(screen) end if case (MPI_TYPE_MPICH) @@ -1515,7 +1514,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) if (stat/=0 .or. .not.success) then call run_mpi_wrapper(wrapper,[string_t('-v')],verbose=verbose, & exitcode=stat,cmd_success=success,screen_output=screen) - call remove_new_lines(screen) + call remove_newline_characters(screen) endif ! Third option: mpiexec --version @@ -1540,7 +1539,7 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) call syntax_error(error,'local INTEL MPI library does not support -v') return else - call remove_new_lines(screen) + call remove_newline_characters(screen) end if case default @@ -1574,35 +1573,4 @@ type(string_t) function mpi_wrapper_query(mpilib,wrapper,command,verbose,error) end function mpi_wrapper_query -! Remove all new line characters from the current string -subroutine remove_new_lines(string) - type(string_t), intent(inout) :: string - - integer :: feed,length - - if (.not.allocated(string%s)) return - - - length = len(string%s) - feed = scan(string%s,new_line('a')) - - do while (length>0 .and. feed>0) - - if (length==1) then - string = string_t("") - elseif (feed==1) then - string%s = string%s(2:length) - elseif (feed==length) then - string%s = string%s(1:length-1) - else - string%s = string%s(1:feed-1)//string%s(feed+1:length) - end if - - length = len(string%s) - feed = scan(string%s,new_line('a')) - - end do - -end subroutine remove_new_lines - end module fpm_meta diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index f8dc4e6daf..404a7dc6f5 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -43,7 +43,7 @@ module fpm_strings public :: to_fortran_name, is_fortran_name public :: string_array_contains, string_cat, len_trim, operator(.in.), fnv_1a public :: replace, resize, str, join, glob -public :: notabs +public :: notabs, remove_newline_characters !> Module naming public :: is_valid_module_name, is_valid_module_prefix, & @@ -1219,6 +1219,46 @@ logical function has_valid_standard_prefix(module_name,package_name) result(vali end function has_valid_standard_prefix +! Remove all new line characters from the current string, replace them with spaces +subroutine remove_newline_characters(string) + type(string_t), intent(inout) :: string + + integer :: feed,length + + character(*), parameter :: CRLF = new_line('a')//achar(13) + character(*), parameter :: SPACE = ' ' + + if (.not.allocated(string%s)) return + + + length = len(string%s) + feed = scan(string%s,CRLF) + + do while (length>0 .and. feed>0) + + ! Remove heading + if (length==1) then + string = string_t("") + + elseif (feed==1) then + string%s = string%s(2:length) + + ! Remove trailing + elseif (feed==length) then + string%s = string%s(1:length-1) + + ! In between: replace with space + else + string%s(feed:feed) = SPACE + end if + + length = len(string%s) + feed = scan(string%s,CRLF) + + end do + +end subroutine remove_newline_characters + !> !!### NAME !! notabs(3f) - [fpm_strings:NONALPHA] expand tab characters From da1a585358d21542bcafff68f2b67700d97ece33 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:42:43 +0200 Subject: [PATCH 463/799] test macOS only --- .github/workflows/meta.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index d88ffc2501..9538fa0789 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -29,17 +29,18 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest - mpi: intel - - os: ubuntu-latest + # - os: ubuntu-latest + # mpi: intel + # - os: ubuntu-latest + # mpi: openmpi + # - os: ubuntu-latest + # mpi: mpich + # - #os: windows-latest + # mpi: msmpi + - os: macos-latest mpi: openmpi - - os: ubuntu-latest - mpi: mpich - - os: windows-latest - mpi: msmpi - # env: - # FC: gfortran - # GCC_V: ${{ matrix.gcc_v }} + env: + FC: gfortran steps: - name: Checkout code From a0f0f59cec0978c56134a73581499a84809e7be0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:51:31 +0200 Subject: [PATCH 464/799] env syntax --- .github/workflows/meta.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 9538fa0789..6aef320a25 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -39,8 +39,6 @@ jobs: # mpi: msmpi - os: macos-latest mpi: openmpi - env: - FC: gfortran steps: - name: Checkout code @@ -132,9 +130,9 @@ jobs: - name: (macOS) Install Homebrew gfortran if: contains(matrix.os, 'macos') run: | - brew install gcc@${GCC_V} - ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran - which gfortran-${GCC_V} + brew install gcc@${{ env.GCC_V }} + ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran + which gfortran-${{ env.GCC_V }} which gfortran - name: (macOS) Install homebrew MPICH @@ -145,7 +143,7 @@ jobs: - name: (macOS) Install homebrew OpenMPI if: contains(matrix.mpi,'openmpi') && contains(matrix.os,'macos') run: | - brew install --cc=gcc-${GCC_V} openmpi + brew install --cc=gcc-${{ env.GCC_V}} openmpi # Phase 1: Bootstrap fpm with existing version - name: Install fpm From fc580e3172ce747017420ed7b38f8798cd6e94b2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:55:18 +0200 Subject: [PATCH 465/799] restore MPICH+macOS, set gcc for C/C++ --- .github/workflows/meta.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 6aef320a25..11e0038239 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -39,6 +39,8 @@ jobs: # mpi: msmpi - os: macos-latest mpi: openmpi + - os: macos-latest + mpi: mpich steps: - name: Checkout code @@ -239,6 +241,14 @@ jobs: echo "FPM_CC=icc" >> $GITHUB_ENV echo "FPM_CXX=icpc" >> $GITHUB_ENV + - name: (macOS) Use gcc/g++ instead of Clang for C/C++ + if: contains(matrix.os,'macOS') + shell: bash + run: | + echo "FPM_FC=gfortran" >> $GITHUB_ENV + echo "FPM_CC=gcc-{{ env.GCC_V }}" >> $GITHUB_ENV + echo "FPM_CXX=g++-{{ env.GCC_V }}" >> $GITHUB_ENV + - name: Run metapackage tests using the release version shell: bash run: | From d9aa848bfbe32d9f7b35e8c50176f31588c7556b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:56:30 +0200 Subject: [PATCH 466/799] remove link gfortran-10 --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 11e0038239..fcdb87bd5e 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -133,7 +133,7 @@ jobs: if: contains(matrix.os, 'macos') run: | brew install gcc@${{ env.GCC_V }} - ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran + # ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran which gfortran-${{ env.GCC_V }} which gfortran From 8a273e7938a9e36d9efc1ed1a7a1f7ba2aa08a2a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 08:57:18 +0200 Subject: [PATCH 467/799] print version --- .github/workflows/meta.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index fcdb87bd5e..6e9c5ca468 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -136,6 +136,8 @@ jobs: # ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran which gfortran-${{ env.GCC_V }} which gfortran + gfortran-${{ env.GCC_V }} --version + gfortran --version - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From c1cfbd5f559b1840c47cbe5a7c30f3d342fa2731 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:01:26 +0200 Subject: [PATCH 468/799] use all gcc-13 compilers --- .github/workflows/meta.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 6e9c5ca468..68170ec0ec 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -19,7 +19,7 @@ env: HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" HOMEBREW_NO_GITHUB_API: "ON" HOMEBREW_NO_INSTALL_CLEANUP: "ON" - GCC_V: "10" + GCC_V: "13" jobs: @@ -133,11 +133,8 @@ jobs: if: contains(matrix.os, 'macos') run: | brew install gcc@${{ env.GCC_V }} - # ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran which gfortran-${{ env.GCC_V }} which gfortran - gfortran-${{ env.GCC_V }} --version - gfortran --version - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') @@ -247,7 +244,7 @@ jobs: if: contains(matrix.os,'macOS') shell: bash run: | - echo "FPM_FC=gfortran" >> $GITHUB_ENV + echo "FPM_FC=gfortran-{{ env.GCC_V }}" >> $GITHUB_ENV echo "FPM_CC=gcc-{{ env.GCC_V }}" >> $GITHUB_ENV echo "FPM_CXX=g++-{{ env.GCC_V }}" >> $GITHUB_ENV From c966c3d90a0d521ae4ab73a1497f7fbff174bd21 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:02:32 +0200 Subject: [PATCH 469/799] missing $ --- .github/workflows/meta.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 68170ec0ec..99bb0211ae 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -244,9 +244,9 @@ jobs: if: contains(matrix.os,'macOS') shell: bash run: | - echo "FPM_FC=gfortran-{{ env.GCC_V }}" >> $GITHUB_ENV - echo "FPM_CC=gcc-{{ env.GCC_V }}" >> $GITHUB_ENV - echo "FPM_CXX=g++-{{ env.GCC_V }}" >> $GITHUB_ENV + echo "FPM_FC=gfortran-${{ env.GCC_V }}" >> $GITHUB_ENV + echo "FPM_CC=gcc-${{ env.GCC_V }}" >> $GITHUB_ENV + echo "FPM_CXX=g++-${{ env.GCC_V }}" >> $GITHUB_ENV - name: Run metapackage tests using the release version shell: bash From 4766e1b9cb2c579ee5414b609d9336673849d4db Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:06:45 +0200 Subject: [PATCH 470/799] get_dos_path: move to `fpm_filesystem` --- src/fpm_filesystem.F90 | 65 +++++++++++++++++++++++++++++++++++++++++- src/fpm_meta.f90 | 65 +----------------------------------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index c7b12a8b5e..91a8124889 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -15,7 +15,7 @@ module fpm_filesystem mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, get_tmp_directory, & - execute_and_read_output + execute_and_read_output, get_dos_path integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -1082,4 +1082,67 @@ subroutine get_tmp_directory(tmp_dir, error) call fatal_error(error, "Couldn't determine system temporary directory.") end + !> Ensure a windows path is converted to a DOS path if it contains spaces + function get_dos_path(path,error) + character(len=*), intent(in) :: path + type(error_t), allocatable, intent(out) :: error + character(len=:), allocatable :: get_dos_path + + character(:), allocatable :: redirect,screen_output,line + integer :: stat,cmdstat,iunit,last + + ! Non-Windows OS + if (get_os_type()/=OS_WINDOWS) then + get_dos_path = path + return + end if + + ! Trim path first + get_dos_path = trim(path) + + !> No need to convert if there are no spaces + has_spaces: if (scan(get_dos_path,' ')>0) then + + redirect = get_temp_filename() + call execute_command_line('cmd /c for %A in ("'//path//'") do @echo %~sA >'//redirect//' 2>&1',& + exitstat=stat,cmdstat=cmdstat) + + !> Read screen output + command_OK: if (cmdstat==0 .and. stat==0) then + + allocate(character(len=0) :: screen_output) + open(newunit=iunit,file=redirect,status='old',iostat=stat) + if (stat == 0)then + + do + call getline(iunit, line, stat) + if (stat /= 0) exit + screen_output = screen_output//line//' ' + end do + + ! Close and delete file + close(iunit,status='delete') + + else + call fatal_error(error,'cannot read temporary file from successful DOS path evaluation') + return + endif + + else command_OK + + call fatal_error(error,'unsuccessful Windows->DOS path command') + return + + end if command_OK + + get_dos_path = trim(adjustl(screen_output)) + + endif has_spaces + + !> Ensure there are no trailing slashes + last = len_trim(get_dos_path) + if (last>1 .and. get_dos_path(last:last)=='/' .or. get_dos_path(last:last)=='\') get_dos_path = get_dos_path(1:last-1) + + end function get_dos_path + end module fpm_filesystem diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e78fe13181..4d10a158b9 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -22,7 +22,7 @@ module fpm_meta use fpm_git, only : git_target_branch use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix -use fpm_filesystem, only: run, get_temp_filename, getline, exists, canon_path, is_dir +use fpm_filesystem, only: run, get_temp_filename, getline, exists, canon_path, is_dir, get_dos_path use fpm_versioning, only: version_t, new_version, regex_version_from_text use fpm_os, only: get_absolute_path use iso_fortran_env, only: stdout => output_unit @@ -880,69 +880,6 @@ subroutine compiler_get_version(self,version,is_msys2,error) end subroutine compiler_get_version -!> Ensure a windows path is converted to a DOS path if it contains spaces -function get_dos_path(path,error) - character(len=*), intent(in) :: path - type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: get_dos_path - - character(:), allocatable :: redirect,screen_output,line - integer :: stat,cmdstat,iunit,last - - ! Non-Windows OS - if (get_os_type()/=OS_WINDOWS) then - get_dos_path = path - return - end if - - ! Trim path first - get_dos_path = trim(path) - - !> No need to convert if there are no spaces - has_spaces: if (scan(get_dos_path,' ')>0) then - - redirect = get_temp_filename() - call execute_command_line('cmd /c for %A in ("'//path//'") do @echo %~sA >'//redirect//' 2>&1',& - exitstat=stat,cmdstat=cmdstat) - - !> Read screen output - command_OK: if (cmdstat==0 .and. stat==0) then - - allocate(character(len=0) :: screen_output) - open(newunit=iunit,file=redirect,status='old',iostat=stat) - if (stat == 0)then - - do - call getline(iunit, line, stat) - if (stat /= 0) exit - screen_output = screen_output//line//' ' - end do - - ! Close and delete file - close(iunit,status='delete') - - else - call fatal_error(error,'cannot read temporary file from successful DOS path evaluation') - return - endif - - else command_OK - - call fatal_error(error,'unsuccessful Windows->DOS path command') - return - - end if command_OK - - get_dos_path = trim(adjustl(screen_output)) - - endif has_spaces - - !> Ensure there are no trailing slashes - last = len_trim(get_dos_path) - if (last>1 .and. get_dos_path(last:last)=='/' .or. get_dos_path(last:last)=='\') get_dos_path = get_dos_path(1:last-1) - -end function get_dos_path - !> Initialize an MPI metapackage from a valid wrapper command ('mpif90', etc...) subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cxx_wrapper,error) class(metapackage_t), intent(inout) :: this From 9184cbfd49e7df312469a3ebf17bb90d743803cd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:10:39 +0200 Subject: [PATCH 471/799] dummy links to gcc-9 for bootstrapping --- .github/workflows/meta.yml | 13 ++++++++++++- src/fpm_filesystem.F90 | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 99bb0211ae..39a38151da 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -135,6 +135,17 @@ jobs: brew install gcc@${{ env.GCC_V }} which gfortran-${{ env.GCC_V }} which gfortran + # Backport gfortran shared libraries to version 9 folder. This is necessary because all macOS releases of fpm + # have these paths hardcoded in the executable (no PIC?). As the gcc ABIs have not changed from 9 to 10, we + # can just create symbolic links for now. This can be removed when an updated fpm release is built with gcc-10 + mkdir /usr/local/opt/gcc@9 + mkdir /usr/local/opt/gcc@9/lib + mkdir /usr/local/opt/gcc@9/lib/gcc + mkdir /usr/local/opt/gcc@9/lib/gcc/9 + mkdir /usr/local/lib/gcc/9 + ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib + ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib + ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') @@ -144,7 +155,7 @@ jobs: - name: (macOS) Install homebrew OpenMPI if: contains(matrix.mpi,'openmpi') && contains(matrix.os,'macos') run: | - brew install --cc=gcc-${{ env.GCC_V}} openmpi + brew install --cc=gcc-${{ env.GCC_V }} openmpi # Phase 1: Bootstrap fpm with existing version - name: Install fpm diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 91a8124889..d2ffb61f0c 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -1082,7 +1082,7 @@ subroutine get_tmp_directory(tmp_dir, error) call fatal_error(error, "Couldn't determine system temporary directory.") end - !> Ensure a windows path is converted to a DOS path if it contains spaces + !> Ensure a windows path is converted to an 8.3 DOS path if it contains spaces function get_dos_path(path,error) character(len=*), intent(in) :: path type(error_t), allocatable, intent(out) :: error From 0611616e5af16cd4a289721b59fbe893f7d77992 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:21:27 +0200 Subject: [PATCH 472/799] fpm bootstrap needs gcc-10 --- .github/workflows/meta.yml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 39a38151da..9a63ea4237 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -135,17 +135,16 @@ jobs: brew install gcc@${{ env.GCC_V }} which gfortran-${{ env.GCC_V }} which gfortran - # Backport gfortran shared libraries to version 9 folder. This is necessary because all macOS releases of fpm - # have these paths hardcoded in the executable (no PIC?). As the gcc ABIs have not changed from 9 to 10, we - # can just create symbolic links for now. This can be removed when an updated fpm release is built with gcc-10 - mkdir /usr/local/opt/gcc@9 - mkdir /usr/local/opt/gcc@9/lib - mkdir /usr/local/opt/gcc@9/lib/gcc - mkdir /usr/local/opt/gcc@9/lib/gcc/9 - mkdir /usr/local/lib/gcc/9 - ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@9/lib/gcc/9/libquadmath.0.dylib - ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@9/lib/gcc/9/libgfortran.5.dylib - ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/9/libgcc_s.1.dylib + # Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm + # have these paths hardcoded in the executable (no PIC?). Current bootstrap version 0.8.0 has gcc-10 + mkdir /usr/local/opt/gcc@10 + mkdir /usr/local/opt/gcc@10/lib + mkdir /usr/local/opt/gcc@10/lib/gcc + mkdir /usr/local/opt/gcc@10/lib/gcc/10 + mkdir /usr/local/lib/gcc/10 + ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib + ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib + ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From 3d2f24218feb21df23d78e65c7b22d3b1f9f7215 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:25:23 +0200 Subject: [PATCH 473/799] remove metapackages from the standard tests --- ci/run_tests.sh | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index bc3de03aee..b0e769b73e 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -201,26 +201,6 @@ EXIT_CODE=0 test $EXIT_CODE -eq 1 popd - -# Test metapackages -pushd metapackage_openmp -EXIT_CODE=0 -"$fpm" build || EXIT_CODE=$? -test $EXIT_CODE -eq 0 -EXIT_CODE=0 -"$fpm" run || EXIT_CODE=$? -test $EXIT_CODE -eq 0 -popd - -pushd metapackage_stdlib -EXIT_CODE=0 -"$fpm" build || EXIT_CODE=$? -test $EXIT_CODE -eq 0 -EXIT_CODE=0 -"$fpm" run || EXIT_CODE=$? -test $EXIT_CODE -eq 0 -popd - # test dependency priority pushd dependency_priority From 36770e555609316cf433d48801aeab9a538a4b8b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:29:42 +0200 Subject: [PATCH 474/799] restore other os/MPI configs --- .github/workflows/meta.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 9a63ea4237..aae8e5a2b7 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -29,14 +29,14 @@ jobs: fail-fast: false matrix: include: - # - os: ubuntu-latest - # mpi: intel - # - os: ubuntu-latest - # mpi: openmpi - # - os: ubuntu-latest - # mpi: mpich - # - #os: windows-latest - # mpi: msmpi + - os: ubuntu-latest + mpi: intel + - os: ubuntu-latest + mpi: openmpi + - os: ubuntu-latest + mpi: mpich + - os: windows-latest + mpi: msmpi - os: macos-latest mpi: openmpi - os: macos-latest From 8a3e37512f35b977963c16d3f2fa11def27c37ee Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:32:43 +0200 Subject: [PATCH 475/799] gcc-13 not available on Ubuntu-latest --- .github/workflows/meta.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index aae8e5a2b7..4add850068 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -31,10 +31,16 @@ jobs: include: - os: ubuntu-latest mpi: intel + env: + GCC_V: "10" - os: ubuntu-latest mpi: openmpi + env: + GCC_V: "10" - os: ubuntu-latest mpi: mpich + env: + GCC_V: "10" - os: windows-latest mpi: msmpi - os: macos-latest From beab3561b5dc8612039c762af425d501bfdf8160 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:39:17 +0200 Subject: [PATCH 476/799] switch GCC_V --- .github/workflows/meta.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 4add850068..64e4cba993 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -19,7 +19,6 @@ env: HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" HOMEBREW_NO_GITHUB_API: "ON" HOMEBREW_NO_INSTALL_CLEANUP: "ON" - GCC_V: "13" jobs: @@ -31,16 +30,10 @@ jobs: include: - os: ubuntu-latest mpi: intel - env: - GCC_V: "10" - os: ubuntu-latest mpi: openmpi - env: - GCC_V: "10" - os: ubuntu-latest mpi: mpich - env: - GCC_V: "10" - os: windows-latest mpi: msmpi - os: macos-latest @@ -52,6 +45,16 @@ jobs: - name: Checkout code uses: actions/checkout@v1 + - name: (Ubuntu) setup gcc version + if: contains(matrix.os,'ubuntu') + run: | + echo "GCC_V=10" >> $GITHUB_ENV + + - name: (macOS) setup gcc version + if: contains(matrix.os,'macos') + run: | + echo "GCC_V=13" >> $GITHUB_ENV + - uses: msys2/setup-msys2@v2 if: contains(matrix.os,'windows') with: From b695b70ba0abde2256b937bdd0c844eb0965e492 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 09:43:30 +0200 Subject: [PATCH 477/799] Windows: also check in the default Microsoft MPI folder --- src/fpm_meta.f90 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 4d10a158b9..0f30ff1ff2 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -581,6 +581,15 @@ logical function msmpi_init(this,compiler,error) result(found) if (allocated(error)) return endif + ! Do a third attempt: search for mpiexec.exe in the default location + if (len_trim(bindir)<=0 .or. .not.exists(bindir) .or. allocated(error)) then + windir = get_dos_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',error) + + if (.not.allocated(error)) & + call find_command_location(windir,bindir,verbose=verbose,error=error) + + endif + if (allocated(error) .or. .not.exists(bindir)) then call fatal_error(error,'MS-MPI error: MS-MPI Runtime directory is missing. '//& 'check environment variable %MSMPI_BIN% or that the folder is in %PATH%.') From 750f0d243d0fc2848e56e2b77823e161d8382210 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 18:32:50 +0200 Subject: [PATCH 478/799] Implement wildcard `"*"` --- example_packages/metapackage_mpi/fpm.toml | 2 +- example_packages/metapackage_mpi_c/fpm.toml | 2 +- example_packages/metapackage_mpi_cpp/fpm.toml | 2 +- example_packages/metapackage_openmp/fpm.toml | 2 +- example_packages/metapackage_stdlib/fpm.toml | 2 +- src/fpm/manifest/meta.f90 | 137 +++++++++++++++--- src/fpm_meta.f90 | 8 +- 7 files changed, 126 insertions(+), 29 deletions(-) diff --git a/example_packages/metapackage_mpi/fpm.toml b/example_packages/metapackage_mpi/fpm.toml index 933e9568cc..fcd1b7e2d5 100644 --- a/example_packages/metapackage_mpi/fpm.toml +++ b/example_packages/metapackage_mpi/fpm.toml @@ -13,7 +13,7 @@ implicit-typing = true auto-executables = true [dependencies] -mpi = true +mpi = "*" [install] library = false diff --git a/example_packages/metapackage_mpi_c/fpm.toml b/example_packages/metapackage_mpi_c/fpm.toml index ffbb88a139..feb1c0297a 100644 --- a/example_packages/metapackage_mpi_c/fpm.toml +++ b/example_packages/metapackage_mpi_c/fpm.toml @@ -14,7 +14,7 @@ implicit-typing=true implicit-external=true [dependencies] -mpi = true +mpi = "*" [install] library = false diff --git a/example_packages/metapackage_mpi_cpp/fpm.toml b/example_packages/metapackage_mpi_cpp/fpm.toml index 5d236bcc89..7edb3cbd23 100644 --- a/example_packages/metapackage_mpi_cpp/fpm.toml +++ b/example_packages/metapackage_mpi_cpp/fpm.toml @@ -14,7 +14,7 @@ implicit-typing=true implicit-external=true [dependencies] -mpi = true +mpi = "*" [install] library = false diff --git a/example_packages/metapackage_openmp/fpm.toml b/example_packages/metapackage_openmp/fpm.toml index 9638da7b42..442f12b84f 100644 --- a/example_packages/metapackage_openmp/fpm.toml +++ b/example_packages/metapackage_openmp/fpm.toml @@ -11,7 +11,7 @@ auto-tests = true auto-examples = true [dependencies] -openmp = true +openmp = "*" [install] library = false diff --git a/example_packages/metapackage_stdlib/fpm.toml b/example_packages/metapackage_stdlib/fpm.toml index b90849bd50..8932b23b2e 100644 --- a/example_packages/metapackage_stdlib/fpm.toml +++ b/example_packages/metapackage_stdlib/fpm.toml @@ -7,7 +7,7 @@ auto-tests = true auto-examples = true [dependencies] -stdlib = true +stdlib = "*" [install] library = false diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index 5a72c96db8..9016932b7c 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -17,24 +17,131 @@ module fpm_manifest_metapackages public :: metapackage_config_t, new_meta_config, is_meta_package + + !> Configuration data for a single metapackage request + type :: metapackage_request_t + + !> Request flag + logical :: on = .false. + + !> Metapackage name + character(len=:), allocatable :: name + + !> Version Specification string + character(len=:), allocatable :: version + + end type metapackage_request_t + + !> Configuration data for metapackages type :: metapackage_config_t !> Request MPI support - logical :: mpi = .false. + type(metapackage_request_t) :: mpi !> Request OpenMP support - logical :: openmp = .false. + type(metapackage_request_t) :: openmp !> Request stdlib support - logical :: stdlib = .false. - + type(metapackage_request_t) :: stdlib end type metapackage_config_t contains + !> Destroy a metapackage request + elemental subroutine request_destroy(self) + + !> Instance of the request + class(metapackage_request_t), intent(inout) :: self + + self%on = .false. + if (allocated(self%version)) deallocate(self%version) + if (allocated(self%name)) deallocate(self%name) + + end subroutine request_destroy + + !> Parse version string of a metapackage reques + subroutine request_parse(self, version_request, error) + + ! Instance of this metapackage + type(metapackage_request_t), intent(inout) :: self + + ! Parse version request + character(len=*), intent(in) :: version_request + + ! Error message + type(error_t), allocatable, intent(out) :: error + + ! wildcard = use any versions + if (version_request=="*") then + + ! Any version is OK + self%on = .true. + self%version = version_request + + else + + call fatal_error(error,'Value <'//version_request//'> for metapackage '//self%name//& + 'is not currently supported. Try "*" instead. ') + return + + end if + + end subroutine request_parse + + !> Construct a new metapackage request from the dependencies table + subroutine new_request(self, key, table, error) + + type(metapackage_request_t), intent(out) :: self + + !> The package name + character(len=*), intent(in) :: key + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + + integer :: stat,i + character(len=:), allocatable :: value + type(toml_key), allocatable :: keys(:) + + call request_destroy(self) + + !> Set name + self%name = key + if (.not.is_meta_package(key)) then + call fatal_error(error,"Error reading fpm.toml: <"//key//"> is not a valid metapackage name") + return + end if + + !> The toml table is not checked here because it already passed + !> the "new_dependencies" check + + call table%get_keys(keys) + + do i=1,size(keys) + if (keys(i)%key==key) then + call get_value(table, key, value) + if (.not. allocated(value)) then + call syntax_error(error, "Could not retrieve version string for metapackage key <"//key//">. Check syntax") + return + else + call request_parse(self, value, error) + return + endif + end if + end do + + ! Key is not present, metapackage not requested + return + + end subroutine new_request + !> Construct a new build configuration from a TOML data structure subroutine new_meta_config(self, table, error) @@ -51,24 +158,14 @@ subroutine new_meta_config(self, table, error) !> The toml table is not checked here because it already passed !> the "new_dependencies" check + call new_request(self%openmp, "openmp", table, error); + if (allocated(error)) return - call get_value(table, "openmp", self%openmp, .false., stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'openmp' in fpm.toml, expecting logical") - return - end if - - call get_value(table, "stdlib", self%stdlib, .false., stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'stdlib' in fpm.toml, expecting logical") - return - end if + call new_request(self%stdlib, "stdlib", table, error) + if (allocated(error)) return - call get_value(table, "mpi", self%mpi, .false., stat=stat) - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'mpi' in fpm.toml, expecting logical") - return - end if + call new_request(self%mpi, "mpi", table, error) + if (allocated(error)) return end subroutine new_meta_config diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 0f30ff1ff2..ac2c7e0693 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -397,24 +397,24 @@ subroutine resolve_metapackage_model(model,package,settings,error) end if ! OpenMP - if (package%meta%openmp) then + if (package%meta%openmp%on) then call add_metapackage_model(model,package,settings,"openmp",error) if (allocated(error)) return endif ! stdlib - if (package%meta%stdlib) then + if (package%meta%stdlib%on) then call add_metapackage_model(model,package,settings,"stdlib",error) if (allocated(error)) return endif ! Stdlib is not 100% thread safe. print a warning to the user - if (package%meta%stdlib .and. package%meta%openmp) then + if (package%meta%stdlib%on .and. package%meta%openmp%on) then write(stdout,'(a)')' both openmp and stdlib requested: some functions may not be thread-safe!' end if ! MPI - if (package%meta%mpi) then + if (package%meta%mpi%on) then call add_metapackage_model(model,package,settings,"mpi",error) if (allocated(error)) return endif From 3ad42220118bb557c9b2d2b9a9798410ba98d25e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 19:47:10 +0200 Subject: [PATCH 479/799] set MSMPI_BIN --- .github/workflows/meta.yml | 6 ++++-- src/fpm_meta.f90 | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 64e4cba993..41a336584c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -113,7 +113,7 @@ jobs: - name: (Windows) download MS-MPI setup (SDK is from MSYS2) if: contains(matrix.os,'windows') - run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.1/msmpisetup.exe + run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.2/msmpisetup.exe - name: (Windows) Install mpiexec.exe (-force needed to bypass GUI on headless) if: contains(matrix.os,'windows') @@ -126,7 +126,9 @@ jobs: - name: (Windows) put MSMPI_BIN on PATH (where mpiexec is) if: contains(matrix.os,'windows') - run: echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + run: | + echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append - name: (Windows) Install MSYS2 msmpi package if: contains(matrix.os,'windows') diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index ac2c7e0693..cdebcbbe71 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -585,6 +585,8 @@ logical function msmpi_init(this,compiler,error) result(found) if (len_trim(bindir)<=0 .or. .not.exists(bindir) .or. allocated(error)) then windir = get_dos_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',error) + print *, 'windir=',windir + if (.not.allocated(error)) & call find_command_location(windir,bindir,verbose=verbose,error=error) From a707c000d1925f999f5d4fe5d9a73c6e202fcbbf Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 19:52:13 +0200 Subject: [PATCH 480/799] remove return --- src/fpm_meta.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index cdebcbbe71..0faa916d19 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -578,7 +578,6 @@ logical function msmpi_init(this,compiler,error) result(found) ! Do a second attempt: search for mpiexec.exe if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) - if (allocated(error)) return endif ! Do a third attempt: search for mpiexec.exe in the default location From 40f2522a3eebc6caa08d6d7fd5664594d2739f5e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 3 May 2023 19:57:00 +0200 Subject: [PATCH 481/799] fix MS-MPI 10.1.2 link --- .github/workflows/meta.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 41a336584c..0b1ab30568 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -113,7 +113,8 @@ jobs: - name: (Windows) download MS-MPI setup (SDK is from MSYS2) if: contains(matrix.os,'windows') - run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.2/msmpisetup.exe + # run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.2/msmpisetup.exe 10.1.1 + run: curl -L -O https://download.microsoft.com/download/a/5/2/a5207ca5-1203-491a-8fb8-906fd68ae623/msmpisetup.exe # 10.1.2 - name: (Windows) Install mpiexec.exe (-force needed to bypass GUI on headless) if: contains(matrix.os,'windows') From 2212c664aed90d97c1534d036a60187b61c3f125 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 4 May 2023 09:14:38 +0200 Subject: [PATCH 482/799] windows Intel CI init --- .github/workflows/meta.yml | 42 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 0b1ab30568..3ab7cb5ee4 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,18 +28,20 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest + #- os: ubuntu-latest + # mpi: intel + #- os: ubuntu-latest + #mpi: openmpi + #- os: ubuntu-latest + #mpi: mpich + #- os: windows-latest + # mpi: msmpi + - os: windows-latest mpi: intel - - os: ubuntu-latest - mpi: openmpi - - os: ubuntu-latest - mpi: mpich - - os: windows-latest - mpi: msmpi - - os: macos-latest - mpi: openmpi - - os: macos-latest - mpi: mpich + #- os: macos-latest + #mpi: openmpi + #- os: macos-latest + #mpi: mpich steps: - name: Checkout code @@ -55,8 +57,9 @@ jobs: run: | echo "GCC_V=13" >> $GITHUB_ENV - - uses: msys2/setup-msys2@v2 - if: contains(matrix.os,'windows') + - name: (Windows) Install MSYS2 + uses: msys2/setup-msys2@v2 + if: contains(matrix.os,'windows') with: msystem: MINGW64 update: true @@ -68,6 +71,9 @@ jobs: curl gcc-fortran + - name: (Windows) Install OneAPI + if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') + - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) run: | @@ -112,27 +118,27 @@ jobs: run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: (Windows) download MS-MPI setup (SDK is from MSYS2) - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') # run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.2/msmpisetup.exe 10.1.1 run: curl -L -O https://download.microsoft.com/download/a/5/2/a5207ca5-1203-491a-8fb8-906fd68ae623/msmpisetup.exe # 10.1.2 - name: (Windows) Install mpiexec.exe (-force needed to bypass GUI on headless) - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') run: .\msmpisetup.exe -unattend -force - name: (Windows) test that mpiexec.exe exists - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') # can't use MSMPI_BIN as Actions doesn't update PATH from msmpisetup.exe run: Test-Path "C:\Program Files\Microsoft MPI\Bin\mpiexec.exe" -PathType leaf - name: (Windows) put MSMPI_BIN on PATH (where mpiexec is) - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') run: | echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append - name: (Windows) Install MSYS2 msmpi package - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') shell: msys2 {0} run: pacman --noconfirm -S mingw-w64-x86_64-msmpi From 84202de84d085af279ad0bea0c671d3e81362724 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 4 May 2023 09:16:46 +0200 Subject: [PATCH 483/799] Update meta.yml --- .github/workflows/meta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 3ab7cb5ee4..95f1d18913 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -73,6 +73,7 @@ jobs: - name: (Windows) Install OneAPI if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') + run: echo "HELLO ONEAPI" - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) From 6540d56d8eca52c111a82f6b8f5993ebeb5f4ec1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 4 May 2023 09:20:20 +0200 Subject: [PATCH 484/799] use intel action --- .github/workflows/meta.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 95f1d18913..c53979b0bc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -59,7 +59,7 @@ jobs: - name: (Windows) Install MSYS2 uses: msys2/setup-msys2@v2 - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') with: msystem: MINGW64 update: true @@ -73,7 +73,11 @@ jobs: - name: (Windows) Install OneAPI if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') - run: echo "HELLO ONEAPI" + uses: awvwgk/setup-fortran@v1 + id: setup-fortran + with: + compiler: intel + version: '2023.1' - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) From 19e4c02b3f32c9bfb56ef98f9ed9a564d0395b38 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 4 May 2023 11:26:01 +0200 Subject: [PATCH 485/799] Update meta.yml --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c53979b0bc..3675bab32b 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -73,7 +73,7 @@ jobs: - name: (Windows) Install OneAPI if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') - uses: awvwgk/setup-fortran@v1 + uses: awvwgk/setup-fortran@main id: setup-fortran with: compiler: intel From 5dda226e23d759178944cee4b36642366fb1e6fe Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 5 May 2023 13:19:49 +0700 Subject: [PATCH 486/799] Use cd in bootstrap mode --- fpm.toml | 1 - src/fpm/manifest.f90 | 29 ----- src/fpm/manifest/preprocess.f90 | 10 -- src/fpm_compiler.F90 | 2 - src/fpm_os.F90 | 42 +++++-- test/fpm_test/test_manifest.f90 | 199 +------------------------------- 6 files changed, 36 insertions(+), 247 deletions(-) diff --git a/fpm.toml b/fpm.toml index 9135415c43..dcd3f27743 100644 --- a/fpm.toml +++ b/fpm.toml @@ -7,7 +7,6 @@ copyright = "2020 fpm contributors" [preprocess] [preprocess.cpp] -export-windows-macro = true macros=["FPM_RELEASE_VERSION={version}"] [dependencies] diff --git a/src/fpm/manifest.f90 b/src/fpm/manifest.f90 index 2cf2ef4699..400660cf83 100644 --- a/src/fpm/manifest.f90 +++ b/src/fpm/manifest.f90 @@ -126,8 +126,6 @@ subroutine get_package_data(package, file, error, apply_defaults) end if end if - call add_fpm_is_windows_macro(package%preprocess) - end subroutine get_package_data @@ -184,31 +182,4 @@ subroutine package_defaults(package, root, error) end subroutine package_defaults - !> Add the FPM_IS_WINDOWS macro if it wasn't already defined. - subroutine add_fpm_is_windows_macro(preprocessors) - !> Preprocessor configurations. - type(preprocess_config_t), allocatable, intent(inout) :: preprocessors(:) - - type(preprocess_config_t) :: new_cpp - integer :: i, j - - if (os_is_unix()) return - if (.not. allocated(preprocessors)) return - do i = 1, size(preprocessors) - if (preprocessors(i)%export_windows_macro) then - if (allocated(preprocessors(i)%macros)) then - ! Do not add if macro is already defined. - do j = 1, size(preprocessors(i)%macros) - if (preprocessors(i)%macros(j)%s == 'FPM_IS_WINDOWS') cycle - end do - ! Macro not found, therefore add it. - preprocessors(i)%macros = [preprocessors(i)%macros, string_t('FPM_IS_WINDOWS')] - else - preprocessors(i)%macros = [string_t('FPM_IS_WINDOWS')] - end if - end if - end do - end - - end module fpm_manifest diff --git a/src/fpm/manifest/preprocess.f90 b/src/fpm/manifest/preprocess.f90 index ac7029945a..26c3c62168 100644 --- a/src/fpm/manifest/preprocess.f90 +++ b/src/fpm/manifest/preprocess.f90 @@ -34,9 +34,6 @@ module fpm_manifest_preprocess !> Macros to be defined for the preprocessor type(string_t), allocatable :: macros(:) - !> Export `FPM_IS_WINDOWS` macro on Windows for the respective preprocessor. - logical :: export_windows_macro = .false. - contains !> Print information on this instance @@ -58,8 +55,6 @@ subroutine new_preprocess_config(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error - integer :: stat - call check(table, error) if (allocated(error)) return @@ -74,11 +69,6 @@ subroutine new_preprocess_config(self, table, error) call get_list(table, "macros", self%macros, error) if (allocated(error)) return - call get_value(table, "export-windows-macro", self%export_windows_macro, .false., stat=stat) - if (stat /= toml_stat%success) then - call syntax_error(error, "'export-windows-macro' must be a boolean."); return - end if - end subroutine new_preprocess_config !> Check local schema for allowed entries diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 0b70d3ca2f..7aeea9bf73 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -773,8 +773,6 @@ function get_id(compiler) result(id) character(len=*), intent(in) :: compiler integer(kind=compiler_enum) :: id - integer :: stat - if (check_compiler(compiler, "gfortran")) then id = id_gcc return diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 0a5784edaa..7590f8a832 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -136,6 +136,10 @@ subroutine c_f_character(rhs, lhs) end subroutine c_f_character !> Determine the canonical, absolute path for the given path. + !> + !> Calls a C routine that uses the `_WIN32` macro to determine the correct function. + !> + !> Cannot be used in bootstrap mode. subroutine get_realpath(path, real_path, error) character(len=*), intent(in) :: path character(len=:), allocatable, intent(out) :: real_path @@ -155,16 +159,7 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) -#ifndef FPM_BOOTSTRAP - ! Use C routine if not in bootstrap mode. ptr = c_realpath(appended_path, cpath, buffersize) -#else -#ifndef FPM_IS_WINDOWS - ptr = realpath(appended_path, cpath) -#else - ptr = fullpath(cpath, appended_path, buffersize) -#endif -#endif if (c_associated(ptr)) then call c_f_character(cpath, real_path) @@ -172,7 +167,7 @@ subroutine get_realpath(path, real_path, error) call fatal_error(error, "Failed to retrieve absolute path for '"//path//"'.") end if - end subroutine get_realpath + end subroutine !> Determine the canonical, absolute path for the given path. !> Expands home folder (~) on both Unix and Windows. @@ -183,6 +178,10 @@ subroutine get_absolute_path(path, absolute_path, error) character(len=:), allocatable :: home +#ifdef FPM_BOOTSTRAP + call get_absolute_path_by_cd(path, absolute_path, error); return +#endif + if (len_trim(path) < 1) then call fatal_error(error, 'Path cannot be empty'); return else if (path(1:1) == '~') then @@ -218,6 +217,29 @@ subroutine get_absolute_path(path, absolute_path, error) end if end subroutine + !> Alternative to `get_absolute_path` that uses `chdir`/`_chdir` to determine the absolute path. + !> + !> `get_absolute_path` is preferred but `get_absolute_path_by_cd` can be used in bootstrap mode. + subroutine get_absolute_path_by_cd(path, absolute_path, error) + character(len=*), intent(in) :: path + character(len=:), allocatable, intent(out) :: absolute_path + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: current_path + + call get_current_directory(current_path, error) + if (allocated(error)) return + + call change_directory(path, error) + if (allocated(error)) return + + call get_current_directory(absolute_path, error) + if (allocated(error)) return + + call change_directory(current_path, error) + if (allocated(error)) return + end subroutine + !> Converts a path to an absolute, canonical path. subroutine convert_to_absolute_path(path, error) character(len=*), intent(inout) :: path diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 06de364340..fc5641f8f9 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1,7 +1,6 @@ !> Define tests for the `fpm_manifest` modules module test_manifest use fpm_filesystem, only: get_temp_filename - use fpm_environment, only: os_is_unix use testsuite, only : new_unittest, unittest_t, error_t, test_failed, check_string use fpm_manifest use fpm_manifest_profile, only: profile_config_t, find_profile @@ -69,12 +68,7 @@ subroutine collect_manifest(tests) & new_unittest("preprocess-wrongkey", test_preprocess_wrongkey, should_fail=.true.), & & new_unittest("preprocessors-empty", test_preprocessors_empty, should_fail=.true.), & & new_unittest("macro-parsing", test_macro_parsing, should_fail=.false.), & - & new_unittest("macro-parsing-dependency", test_macro_parsing_dependency, should_fail=.false.), & - & new_unittest("not-add-windows-macro", test_not_add_windows_macro), & - & new_unittest("add-windows-macro-with-empty-macros", test_add_windows_macro_with_empty_macros), & - & new_unittest("add-windows-macro-to-fpp", test_add_windows_macro_to_fpp), & - & new_unittest("add-windows-macro-with-other-macro", test_add_windows_macro_with_other_macro), & - & new_unittest("add-second-windows-macro", test_add_second_windows_macro) & + & new_unittest("macro-parsing-dependency", test_macro_parsing_dependency, should_fail=.false.) & & ] end subroutine collect_manifest @@ -1340,7 +1334,7 @@ subroutine test_macro_parsing(error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package - character(:), allocatable :: temp_file, macros, expected_result + character(:), allocatable :: temp_file integer :: unit integer(compiler_enum) :: id @@ -1359,13 +1353,8 @@ subroutine test_macro_parsing(error) if (allocated(error)) return - macros = get_macros(id, package%preprocess(1)%macros, package%version%s()) - - expected_result = " -DFOO -DBAR=2 -DVERSION=0.1.0" - if (.not. os_is_unix()) expected_result = expected_result // " -DFPM_IS_WINDOWS" - - if (macros /= expected_result) then - call test_failed(error, "Macros were not parsed correctly: '"//macros//"'") + if (get_macros(id, package%preprocess(1)%macros, package%version%s()) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then + call test_failed(error, "Macros were not parsed correctly") end if end subroutine test_macro_parsing @@ -1428,184 +1417,4 @@ subroutine test_macro_parsing_dependency(error) end subroutine test_macro_parsing_dependency - !> Not add `FPM_IS_WINDOWS` macro without flag. - subroutine test_not_add_windows_macro(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(package_config_t) :: package - character(:), allocatable :: temp_file - integer :: unit - - allocate(temp_file, source=get_temp_filename()) - - open(file=temp_file, newunit=unit) - write(unit, '(a)') & - & 'name = "example"', & - & 'version = "0.1.0"' - close(unit) - - call get_package_data(package, temp_file, error) - open(file=temp_file, newunit=unit) - close(unit, status='delete') - if (allocated(error)) return - - if (allocated(package%preprocess)) call test_failed(error, 'Preprocess table should not be allocated.') - end - - !> Add `FPM_IS_WINDOWS` macro with empty macros table. - subroutine test_add_windows_macro_with_empty_macros(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(package_config_t) :: package - character(:), allocatable :: temp_file - integer :: unit - - allocate(temp_file, source=get_temp_filename()) - - open(file=temp_file, newunit=unit) - write(unit, '(a)') & - & 'name = "example"', & - & 'version = "0.1.0"', & - & '[preprocess]', & - & '[preprocess.cpp]', & - & 'export-windows-macro = true', & - & 'macros = []' - close(unit) - - call get_package_data(package, temp_file, error) - open(file=temp_file, newunit=unit) - close(unit, status='delete') - if (allocated(error)) return - - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - - if (os_is_unix()) then - if (size(package%preprocess(1)%macros) /= 0) call test_failed(error, "Macros not empty.") - else - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") - end if - end - - !> Add `FPM_IS_WINDOWS` macro to an fpp table. - subroutine test_add_windows_macro_to_fpp(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(package_config_t) :: package - character(:), allocatable :: temp_file - integer :: unit - - allocate(temp_file, source=get_temp_filename()) - - open(file=temp_file, newunit=unit) - write(unit, '(a)') & - & 'name = "example"', & - & 'version = "0.1.0"', & - & '[preprocess]', & - & '[preprocess.fpp]', & - & 'export-windows-macro = true', & - & 'macros = []' - close(unit) - - call get_package_data(package, temp_file, error) - open(file=temp_file, newunit=unit) - close(unit, status='delete') - if (allocated(error)) return - - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name == 'cpp') call test_failed(error, "cpp wasn't defined.") - if (package%preprocess(1)%name /= 'fpp') call test_failed(error, "Preprocessor isn't fpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - - if (os_is_unix()) then - if (size(package%preprocess(1)%macros) /= 0) call test_failed(error, "Macros not empty.") - else - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") - end if - end - - !> Add `FPM_IS_WINDOWS` macro to list of macros containing another macro. - subroutine test_add_windows_macro_with_other_macro(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(package_config_t) :: package - character(:), allocatable :: temp_file - integer :: unit - - allocate(temp_file, source=get_temp_filename()) - - open(file=temp_file, newunit=unit) - write(unit, '(a)') & - & 'name = "example"', & - & 'version = "0.1.0"', & - & '[preprocess]', & - & '[preprocess.cpp]', & - & 'export-windows-macro = true', & - & 'macros = ["ABC"]' - close(unit) - - call get_package_data(package, temp_file, error) - open(file=temp_file, newunit=unit) - close(unit, status='delete') - if (allocated(error)) return - - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - - if (os_is_unix()) then - if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Wrong number of macros.") - else - if (size(package%preprocess(1)%macros) /= 2) call test_failed(error, "Wrong number of macros.") - if (package%preprocess(1)%macros(2)%s /= 'FPM_IS_WINDOWS') call test_failed(error, "'FPM_IS_WINDOWS' not added.") - end if - end - - !> Add `FPM_IS_WINDOWS` macro to list of macros that already contains "FPM_IS_WINDOWS". - subroutine test_add_second_windows_macro(error) - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - type(package_config_t) :: package - character(:), allocatable :: temp_file - integer :: unit - - allocate(temp_file, source=get_temp_filename()) - - open(file=temp_file, newunit=unit) - write(unit, '(a)') & - & 'name = "example"', & - & 'version = "0.1.0"', & - & '[preprocess]', & - & '[preprocess.cpp]', & - & 'export-windows-macro = true', & - & 'macros = ["FPM_IS_WINDOWS"]' - close(unit) - - call get_package_data(package, temp_file, error) - open(file=temp_file, newunit=unit) - close(unit, status='delete') - if (allocated(error)) return - - if (.not. allocated(package%preprocess)) call test_failed(error, 'Preprocess table not allocated.') - if (size(package%preprocess) /= 1) call test_failed(error, 'Wrong number of preprocessors.') - if (package%preprocess(1)%name /= 'cpp') call test_failed(error, "Preprocessor isn't cpp.") - if (.not. allocated(package%preprocess(1)%macros)) call test_failed(error, 'List of macros not allocated.') - if (size(package%preprocess(1)%macros) /= 1) call test_failed(error, "Macro should not have been added.") - if (package%preprocess(1)%macros(1)%s /= 'FPM_IS_WINDOWS') call test_failed(error, '"FPM_IS_WINDOWS" should exist.') - end - end module test_manifest From c4cb6883e7082d55019bb14e3f8733fb8bdaf945 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 5 May 2023 13:30:59 +0700 Subject: [PATCH 487/799] Not compile C code in bootstrap mode --- src/fpm_os.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 7590f8a832..4cbda99f10 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -159,7 +159,9 @@ subroutine get_realpath(path, real_path, error) allocate (cpath(buffersize)) +#ifndef FPM_BOOTSTRAP ptr = c_realpath(appended_path, cpath, buffersize) +#endif if (c_associated(ptr)) then call c_f_character(cpath, real_path) From a4ac1dbf4feb4b1e7471c06fdd2044cdb694baa9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 5 May 2023 01:56:13 -0500 Subject: [PATCH 488/799] Download Intel OneAPI installer --- .github/workflows/meta.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 3675bab32b..5647dc1ef9 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -73,12 +73,11 @@ jobs: - name: (Windows) Install OneAPI if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') - uses: awvwgk/setup-fortran@main - id: setup-fortran - with: - compiler: intel - version: '2023.1' - + shell: pwsh + working-directory: C:\TEMP + run: | + curl.exe --output .\webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 + - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) run: | From ace990ee19fca4a508c528b5fdf8557d265c8266 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 5 May 2023 02:02:18 -0500 Subject: [PATCH 489/799] finish installing, delete temp --- .github/workflows/meta.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 5647dc1ef9..7e63a621cf 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -76,7 +76,10 @@ jobs: shell: pwsh working-directory: C:\TEMP run: | - curl.exe --output .\webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 + curl.exe --output webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 + start /b /wait webimage.exe -s -x -f webimage_extracted --log extract.log + del webimage.exe + more extract.log - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) @@ -117,7 +120,7 @@ jobs: printenv >> $GITHUB_ENV - name: (Windows) Put MSYS2_MinGW64 on PATH - if: contains(matrix.os,'windows') + if: contains(matrix.os,'windows') && !contains(matrix.mpi,'intel') # there is not yet an environment variable for this path from msys2/setup-msys2 run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append From fc12ee9fa56e512f46475667cbaf099643adcf0c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 5 May 2023 02:06:49 -0500 Subject: [PATCH 490/799] Update meta.yml --- .github/workflows/meta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 7e63a621cf..8ade0ec225 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -77,8 +77,8 @@ jobs: working-directory: C:\TEMP run: | curl.exe --output webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 - start /b /wait webimage.exe -s -x -f webimage_extracted --log extract.log - del webimage.exe + Start-Process -FilePath "webimage.exe" -ArgumentList "-s -x -f webimage_extracted --log extract.log" -Wait + Remove-Item "webimage.exe" -Force more extract.log - name: (Ubuntu) Install gfortran From c75d99094cfdf9ba4d73317acc6d204a6c06240e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 5 May 2023 02:17:31 -0500 Subject: [PATCH 491/799] install --- .github/workflows/meta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 8ade0ec225..a27f48e3c6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -80,6 +80,7 @@ jobs: Start-Process -FilePath "webimage.exe" -ArgumentList "-s -x -f webimage_extracted --log extract.log" -Wait Remove-Item "webimage.exe" -Force more extract.log + Start-Process -FilePath "webimage_extracted\bootstrapper.exe" -ArgumentList "-s --action install --eula=accept --components=""intel.oneapi.win.cpp-compiler:intel.oneapi.win.ifort-compiler:intel.oneapi.win.mpi.devel"" -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." -Wait - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) From 2ff6924c39679eaaa7d8c773b7b5a7a7175f2157 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 5 May 2023 14:18:46 +0700 Subject: [PATCH 492/799] Add some tests --- src/fpm_os.F90 | 4 +- test/fpm_test/test_os.f90 | 91 ++++++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 16 deletions(-) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index 4cbda99f10..f0e2f5437e 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -3,9 +3,11 @@ module fpm_os use fpm_filesystem, only: exists, join_path, get_home use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error + implicit none private - public :: change_directory, get_current_directory, get_absolute_path, convert_to_absolute_path + public :: change_directory, get_current_directory, get_absolute_path, convert_to_absolute_path, & + & get_absolute_path_by_cd integer(c_int), parameter :: buffersize = 1000_c_int diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index c3b7c2e2af..a2536e63d7 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -2,7 +2,7 @@ module test_os use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: env_variable, join_path, mkdir, os_delete_dir, is_dir, get_local_prefix, get_home use fpm_environment, only: os_is_unix - use fpm_os, only: get_absolute_path + use fpm_os, only: get_absolute_path, get_absolute_path_by_cd implicit none private @@ -27,7 +27,11 @@ subroutine collect_os(tests) & new_unittest('tilde-nonexistent-path', tilde_nonexistent_path, should_fail=.true.), & & new_unittest('abs-path-nonexisting', abs_path_nonexisting, should_fail=.true.), & & new_unittest('abs-path-root', abs_path_root), & - & new_unittest('abs-path-home', abs_path_home) & + & new_unittest('abs-path-home', abs_path_home), & + & new_unittest('abs-path-cd-root', abs_path_home), & + & new_unittest('abs-path-cd-home', abs_path_cd_home), & + & new_unittest('abs-path-cd-current', abs_path_cd_current), & + & new_unittest('abs-path-cd-tmp', abs_path_home) & ] end subroutine collect_os @@ -126,19 +130,17 @@ subroutine abs_path_nonexisting(error) call get_absolute_path('/abcde', result, error) end - !> Testing the most obvious absolute path: The root directory. + !> Get the absolute path of the root directory. subroutine abs_path_root(error) type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: result - character(len=:), allocatable :: home_drive - character(len=:), allocatable :: home_path + + character(len=:), allocatable :: home_drive, home_path, result if (os_is_unix()) then call get_absolute_path('/', result, error) if (result /= '/') then - call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'") - return + call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'"); return end if else call env_variable(home_drive, 'HOMEDRIVE') @@ -147,17 +149,16 @@ subroutine abs_path_root(error) call get_absolute_path(home_path, result, error) if (result /= home_path) then - call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'") - return + call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'"); return end if end if end - !> Testing an absolute path which is not root. It should not be altered. + !> Get the absolute path of the home directory. subroutine abs_path_home(error) type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: result - character(len=:), allocatable :: home + + character(len=:), allocatable :: home, result call get_home(home, error) if (allocated(error)) return @@ -166,8 +167,68 @@ subroutine abs_path_home(error) if (allocated(error)) return if (result /= home) then - call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'") - return + call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'"); return + end if + end + + !> Get the absolute path of the root directory using `getcwd`/`_getcwd`. + subroutine abs_path_cd_root(error) + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: home_drive, home_path, result + + if (os_is_unix()) then + call get_absolute_path_by_cd('/', result, error) + + if (result /= '/') then + call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'"); return + end if + else + call env_variable(home_drive, 'HOMEDRIVE') + home_path = home_drive//'\' + + call get_absolute_path(home_path, result, error) + + if (result /= home_path) then + call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'"); return + end if + end if + end + + !> Get the absolute path of the root directory using `getcwd`/`_getcwd`. + subroutine abs_path_cd_home(error) + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: home, result + + call get_home(home, error) + if (allocated(error)) return + + call get_absolute_path_by_cd(home, result, error) + if (allocated(error)) return + + if (result /= home) then + call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'"); return + end if + end + + !> Get the absolute path of the current directory using `getcwd`/`_getcwd`. + subroutine abs_path_cd_current(error) + type(error_t), allocatable, intent(out) :: error + + character(len=:), allocatable :: current_dir, result + + if (os_is_unix()) then + call env_variable(current_dir, 'PWD') + else + call env_variable(current_dir, 'CD') + end if + + call get_absolute_path_by_cd('.', result, error) + if (allocated(error)) return + + if (result /= current_dir) then + call test_failed(error, "Result '"//result//"' doesn't equal current directory '"//current_dir//"'"); return end if end From 8b3f559e26ad3b6cf74e9905ea2c130c17aff68e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 5 May 2023 14:23:51 +0700 Subject: [PATCH 493/799] Use get_current_directory --- test/fpm_test/test_os.f90 | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index a2536e63d7..334fd9edca 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -2,7 +2,7 @@ module test_os use testsuite, only: new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: env_variable, join_path, mkdir, os_delete_dir, is_dir, get_local_prefix, get_home use fpm_environment, only: os_is_unix - use fpm_os, only: get_absolute_path, get_absolute_path_by_cd + use fpm_os, only: get_absolute_path, get_absolute_path_by_cd, get_current_directory implicit none private @@ -30,8 +30,7 @@ subroutine collect_os(tests) & new_unittest('abs-path-home', abs_path_home), & & new_unittest('abs-path-cd-root', abs_path_home), & & new_unittest('abs-path-cd-home', abs_path_cd_home), & - & new_unittest('abs-path-cd-current', abs_path_cd_current), & - & new_unittest('abs-path-cd-tmp', abs_path_home) & + & new_unittest('abs-path-cd-current', abs_path_cd_current) & ] end subroutine collect_os @@ -218,11 +217,8 @@ subroutine abs_path_cd_current(error) character(len=:), allocatable :: current_dir, result - if (os_is_unix()) then - call env_variable(current_dir, 'PWD') - else - call env_variable(current_dir, 'CD') - end if + call get_current_directory(current_dir, error) + if (allocated(error)) return call get_absolute_path_by_cd('.', result, error) if (allocated(error)) return From ca9eb39a01df570605833fac2c48d8be196f8bae Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 5 May 2023 14:40:03 +0700 Subject: [PATCH 494/799] Check that we're still in the same directory --- test/fpm_test/test_os.f90 | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index 334fd9edca..d573ac0b78 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -174,7 +174,10 @@ subroutine abs_path_home(error) subroutine abs_path_cd_root(error) type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: home_drive, home_path, result + character(len=:), allocatable :: home_drive, home_path, current_dir_before, current_dir_after, result + + call get_current_directory(current_dir_before, error) + if (allocated(error)) return if (os_is_unix()) then call get_absolute_path_by_cd('/', result, error) @@ -186,19 +189,30 @@ subroutine abs_path_cd_root(error) call env_variable(home_drive, 'HOMEDRIVE') home_path = home_drive//'\' - call get_absolute_path(home_path, result, error) + call get_absolute_path_by_cd(home_path, result, error) if (result /= home_path) then call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'"); return end if end if + + call get_current_directory(current_dir_after, error) + if (allocated(error)) return + + if (current_dir_before /= current_dir_after) then + call test_failed(error, "Current directory before getting absolute path '"//current_dir_before// & + & "' doesn't equal current directory after getting absolute path '"//current_dir_after//"'."); return + end if end !> Get the absolute path of the root directory using `getcwd`/`_getcwd`. subroutine abs_path_cd_home(error) type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: home, result + character(len=:), allocatable :: home, current_dir_before, current_dir_after, result + + call get_current_directory(current_dir_before, error) + if (allocated(error)) return call get_home(home, error) if (allocated(error)) return @@ -206,6 +220,14 @@ subroutine abs_path_cd_home(error) call get_absolute_path_by_cd(home, result, error) if (allocated(error)) return + call get_current_directory(current_dir_after, error) + if (allocated(error)) return + + if (current_dir_before /= current_dir_after) then + call test_failed(error, "Current directory before getting absolute path '"//current_dir_before// & + & "' doesn't equal current directory after getting absolute path '"//current_dir_after//"'."); return + end if + if (result /= home) then call test_failed(error, "Result '"//result//"' doesn't equal home directory '"//home//"'"); return end if From fb0ad935f48b96c7cee2834062b20f157ea43eef Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 5 May 2023 03:03:29 -0500 Subject: [PATCH 495/799] add environment variables --- .github/workflows/meta.yml | 52 +++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index a27f48e3c6..e4d5e44d5a 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -71,16 +71,16 @@ jobs: curl gcc-fortran - - name: (Windows) Install OneAPI + - name: (Windows) Retrieve Intel toolchain if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') shell: pwsh working-directory: C:\TEMP run: | curl.exe --output webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 - Start-Process -FilePath "webimage.exe" -ArgumentList "-s -x -f webimage_extracted --log extract.log" -Wait + Start-Process -FilePath "webimage.exe" -ArgumentList "-s -x -f oneAPI --log extract.log" -Wait Remove-Item "webimage.exe" -Force - more extract.log - Start-Process -FilePath "webimage_extracted\bootstrapper.exe" -ArgumentList "-s --action install --eula=accept --components=""intel.oneapi.win.cpp-compiler:intel.oneapi.win.ifort-compiler:intel.oneapi.win.mpi.devel"" -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." -Wait + Start-Process -FilePath "oneAPI\bootstrapper.exe" -ArgumentList "-s --action install --eula=accept --components=""intel.oneapi.win.cpp-compiler:intel.oneapi.win.ifort-compiler:intel.oneapi.win.mpi.devel"" -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." -Wait + Remove-Item "oneAPI" -Force -Recurse - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) @@ -139,12 +139,24 @@ jobs: # can't use MSMPI_BIN as Actions doesn't update PATH from msmpisetup.exe run: Test-Path "C:\Program Files\Microsoft MPI\Bin\mpiexec.exe" -PathType leaf + - name: (Windows) test that OneAPI is installed + if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') + run: | + Test-Path -Path "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" -PathType leaf + Test-Path -Path "C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat" -PathType leaf + - name: (Windows) put MSMPI_BIN on PATH (where mpiexec is) if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') run: | echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: (Windows) load OneAPI environment variables + if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') + run: | + echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: (Windows) Install MSYS2 msmpi package if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') shell: msys2 {0} @@ -194,6 +206,22 @@ jobs: mv $(which fpm) fpm-bootstrap${{ matrix.exe }} echo "BOOTSTRAP=$PWD/fpm-bootstrap" >> $GITHUB_ENV + - name: Use Intel compiler for the metapackage tests + if: contains(matrix.mpi,'intel') + shell: bash + run: | + echo "FPM_FC=ifort" >> $GITHUB_ENV + echo "FPM_CC=icc" >> $GITHUB_ENV + echo "FPM_CXX=icpc" >> $GITHUB_ENV + + - name: (macOS) Use gcc/g++ instead of Clang for C/C++ + if: contains(matrix.os,'macOS') + shell: bash + run: | + echo "FPM_FC=gfortran-${{ env.GCC_V }}" >> $GITHUB_ENV + echo "FPM_CC=gcc-${{ env.GCC_V }}" >> $GITHUB_ENV + echo "FPM_CXX=g++-${{ env.GCC_V }}" >> $GITHUB_ENV + - name: Build Fortran fpm (bootstrap) shell: bash run: | @@ -268,22 +296,6 @@ jobs: env: EXE: fpm-${{ env.VERSION }}-${{ matrix.os-arch }}${{ matrix.exe }} - - name: (Ubuntu) Use Intel compiler for the metapackage tests - if: contains(matrix.os,'ubuntu') && contains(matrix.mpi,'intel') - shell: bash - run: | - echo "FPM_FC=ifort" >> $GITHUB_ENV - echo "FPM_CC=icc" >> $GITHUB_ENV - echo "FPM_CXX=icpc" >> $GITHUB_ENV - - - name: (macOS) Use gcc/g++ instead of Clang for C/C++ - if: contains(matrix.os,'macOS') - shell: bash - run: | - echo "FPM_FC=gfortran-${{ env.GCC_V }}" >> $GITHUB_ENV - echo "FPM_CC=gcc-${{ env.GCC_V }}" >> $GITHUB_ENV - echo "FPM_CXX=g++-${{ env.GCC_V }}" >> $GITHUB_ENV - - name: Run metapackage tests using the release version shell: bash run: | From ebd233113381d59c2421e360d09b229653c5ad33 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 5 May 2023 04:25:41 -0500 Subject: [PATCH 496/799] set Intel environment --- .github/workflows/meta.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index e4d5e44d5a..a110b9102e 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -70,6 +70,10 @@ jobs: unzip curl gcc-fortran + + - name: (Windows) Setup VS Build environment + if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') + uses: seanmiddleditch/gha-setup-vsdevenv@master - name: (Windows) Retrieve Intel toolchain if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') @@ -153,9 +157,10 @@ jobs: - name: (Windows) load OneAPI environment variables if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') + shell: cmd run: | - echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append + "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + "C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat" - name: (Windows) Install MSYS2 msmpi package if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') From 49a650310aaec307e238d9b091d8d9905f21fba5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 5 May 2023 17:39:09 +0700 Subject: [PATCH 497/799] Remove redundant key --- src/fpm/manifest/preprocess.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/manifest/preprocess.f90 b/src/fpm/manifest/preprocess.f90 index 26c3c62168..538652c29a 100644 --- a/src/fpm/manifest/preprocess.f90 +++ b/src/fpm/manifest/preprocess.f90 @@ -90,7 +90,7 @@ subroutine check(table, error) do ikey = 1, size(list) select case(list(ikey)%key) !> Valid keys. - case("suffixes", "directories", "macros", "export-windows-macro") + case("suffixes", "directories", "macros") case default call syntax_error(error, "Key '"//list(ikey)%key//"' not allowed in preprocessor '"//name//"'."); exit end select From dd90f55ed93f9251d037e066b14a31574ab0bfcd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 6 May 2023 06:43:34 +0200 Subject: [PATCH 498/799] turn off Intel + Windows action for now --- .github/workflows/meta.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index a110b9102e..90adcf516d 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,20 +28,20 @@ jobs: fail-fast: false matrix: include: - #- os: ubuntu-latest - # mpi: intel - #- os: ubuntu-latest - #mpi: openmpi - #- os: ubuntu-latest - #mpi: mpich - #- os: windows-latest - # mpi: msmpi - - os: windows-latest + - os: ubuntu-latest mpi: intel - #- os: macos-latest - #mpi: openmpi - #- os: macos-latest - #mpi: mpich + - os: ubuntu-latest + mpi: openmpi + - os: ubuntu-latest + mpi: mpich + - os: windows-latest + mpi: msmpi + # - os: windows-latest + # mpi: intel + - os: macos-latest + mpi: openmpi + - os: macos-latest + mpi: mpich steps: - name: Checkout code From fadc272de0dc493279d7d2a88c164c23d1eb28a7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 6 May 2023 07:04:15 +0200 Subject: [PATCH 499/799] do not run fpm tests with Intel compiler here --- .github/workflows/meta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 90adcf516d..00ca63be13 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -240,6 +240,7 @@ jobs: ${{ env.BOOTSTRAP }} run -- --help - name: Test Fortran fpm (bootstrap) + if: !contains(matrix.mpi,'intel') shell: bash run: | ${{ env.BOOTSTRAP }} test From 06ebcfd8554562186aec2180443a52ebdc42c3b2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 6 May 2023 10:55:09 +0200 Subject: [PATCH 500/799] add c/c++ linker flags for `pgif90`/`nvfortran` --- src/fpm_compiler.F90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index e46032503d..c251a9622a 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -690,6 +690,8 @@ subroutine get_main_flags(self, language, flags) flags = '-nofor-main' case(id_intel_classic_windows,id_intel_llvm_windows) flags = '/nofor-main' + case (id_pgi,id_nvhpc) + flags = '-Mnomain' end select case("c++","cpp","cxx") @@ -699,6 +701,8 @@ subroutine get_main_flags(self, language, flags) flags = '-nofor-main' case(id_intel_classic_windows,id_intel_llvm_windows) flags = '/nofor-main' + case (id_pgi,id_nvhpc) + flags = '-Mnomain' end select case default From c461c64530dd7b122d6a8775696a0d883bde650a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 6 May 2023 10:56:54 +0200 Subject: [PATCH 501/799] comment --- src/fpm_targets.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index 21cda7403a..9c2ccc07cd 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -309,6 +309,7 @@ subroutine build_target_list(targets,model) associate(target => targets(size(targets))%ptr) + ! Linker-only flags are necessary on some compilers for codes with non-Fortran main select case (exe_type) case (FPM_TARGET_C_OBJECT) call model%compiler%get_main_flags("c",compile_flags) From 5cbc90a3f9e29444bca6e2da74f1b574a665ef73 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 6 May 2023 11:12:58 +0200 Subject: [PATCH 502/799] Update meta.yml --- .github/workflows/meta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 00ca63be13..53fcd1c2e1 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -125,7 +125,7 @@ jobs: printenv >> $GITHUB_ENV - name: (Windows) Put MSYS2_MinGW64 on PATH - if: contains(matrix.os,'windows') && !contains(matrix.mpi,'intel') + if: contains(matrix.os,'windows') && (!contains(matrix.mpi,'intel')) # there is not yet an environment variable for this path from msys2/setup-msys2 run: echo "${{ runner.temp }}/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append @@ -240,7 +240,7 @@ jobs: ${{ env.BOOTSTRAP }} run -- --help - name: Test Fortran fpm (bootstrap) - if: !contains(matrix.mpi,'intel') + if: (!contains(matrix.mpi,'intel')) shell: bash run: | ${{ env.BOOTSTRAP }} test From 76b34ea1f355464375219113b59c03a73fab0f7f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 7 May 2023 10:48:30 +0200 Subject: [PATCH 503/799] metapackage manifests shortened --- example_packages/metapackage_mpi/fpm.toml | 21 +++--------------- example_packages/metapackage_mpi_c/fpm.toml | 17 +++----------- example_packages/metapackage_mpi_cpp/fpm.toml | 22 +++++-------------- example_packages/metapackage_openmp/fpm.toml | 16 +------------- example_packages/metapackage_stdlib/fpm.toml | 13 +---------- 5 files changed, 13 insertions(+), 76 deletions(-) diff --git a/example_packages/metapackage_mpi/fpm.toml b/example_packages/metapackage_mpi/fpm.toml index fcd1b7e2d5..8588e99979 100644 --- a/example_packages/metapackage_mpi/fpm.toml +++ b/example_packages/metapackage_mpi/fpm.toml @@ -1,19 +1,4 @@ name = "test_mpi" -version = "0.1.0" -license = "license" -author = "Federico Perini" -maintainer = "federico.perini@hello.world" -copyright = "Copyright 2023, Federico Perini and the fpm maintainers" - -[fortran] -implicit-external = true -implicit-typing = true - -[build] -auto-executables = true - -[dependencies] -mpi = "*" - -[install] -library = false +dependencies.mpi = "*" +fortran.implicit-external=true +fortran.implicit-typing=true diff --git a/example_packages/metapackage_mpi_c/fpm.toml b/example_packages/metapackage_mpi_c/fpm.toml index feb1c0297a..67f4e99918 100644 --- a/example_packages/metapackage_mpi_c/fpm.toml +++ b/example_packages/metapackage_mpi_c/fpm.toml @@ -1,20 +1,9 @@ name = "test_mpi_c" -version = "0.1.0" -license = "license" -author = "Federico Perini" -maintainer = "federico.perini@hello.world" -copyright = "Copyright 2023, Federico Perini and the fpm maintainers" +dependencies.mpi = "*" +fortran.implicit-typing=true +fortran.implicit-external=true [[executable]] name = "test-mpi-c-main" main = "main.c" -[fortran] -implicit-typing=true -implicit-external=true - -[dependencies] -mpi = "*" - -[install] -library = false diff --git a/example_packages/metapackage_mpi_cpp/fpm.toml b/example_packages/metapackage_mpi_cpp/fpm.toml index 7edb3cbd23..4fef8f710c 100644 --- a/example_packages/metapackage_mpi_cpp/fpm.toml +++ b/example_packages/metapackage_mpi_cpp/fpm.toml @@ -1,20 +1,8 @@ name = "test_mpi_cpp" -version = "0.1.0" -license = "license" -author = "Federico Perini" -maintainer = "federico.perini@hello.world" -copyright = "Copyright 2023, Federico Perini and the fpm maintainers" +dependencies.mpi="*" +fortran.implicit-typing=true +fortran.implicit-external=true [[executable]] -name = "test-mpi-cpp" -main = "main.cpp" - -[fortran] -implicit-typing=true -implicit-external=true - -[dependencies] -mpi = "*" - -[install] -library = false +name="test-mpi-cpp" +main="main.cpp" diff --git a/example_packages/metapackage_openmp/fpm.toml b/example_packages/metapackage_openmp/fpm.toml index 442f12b84f..f22f381100 100644 --- a/example_packages/metapackage_openmp/fpm.toml +++ b/example_packages/metapackage_openmp/fpm.toml @@ -1,17 +1,3 @@ name = "test_openmp" -version = "0.1.0" -license = "license" -author = "Federico Perini" -maintainer = "federico.perini@hello.world" -copyright = "Copyright 2023, Federico Perini and the fpm maintainers" +dependencies.openmp = "*" -[build] -auto-executables = true -auto-tests = true -auto-examples = true - -[dependencies] -openmp = "*" - -[install] -library = false diff --git a/example_packages/metapackage_stdlib/fpm.toml b/example_packages/metapackage_stdlib/fpm.toml index 8932b23b2e..3e4e8efe66 100644 --- a/example_packages/metapackage_stdlib/fpm.toml +++ b/example_packages/metapackage_stdlib/fpm.toml @@ -1,13 +1,2 @@ name = "test_stdlib" -version = "0.1.0" - -[build] -auto-executables = true -auto-tests = true -auto-examples = true - -[dependencies] -stdlib = "*" - -[install] -library = false +dependencies.stdlib = "*" From 16dc73c555431de4a85b51f2fe2c7ac339f02cd4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 9 May 2023 20:05:26 +0700 Subject: [PATCH 504/799] Not store tmp data in .local --- src/fpm/dependency.f90 | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index c571e41a8d..8beb8ae0db 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -58,7 +58,8 @@ module fpm_dependency use, intrinsic :: iso_fortran_env, only: output_unit use fpm_environment, only: get_os_type, OS_WINDOWS, os_is_unix use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, os_delete_dir + use fpm_filesystem, only: exists, join_path, mkdir, canon_path, windows_path, list_files, is_dir, basename, & + os_delete_dir, get_temp_filename use fpm_git, only: git_target_revision, git_target_default, git_revision, operator(==) use fpm_manifest, only: package_config_t, dependency_config_t, get_package_data use fpm_manifest_dependency, only: manifest_has_changed @@ -637,7 +638,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade !> Downloader instance. class(downloader_t), optional, intent(in) :: downloader_ - character(:), allocatable :: cache_path, target_url, tmp_pkg_path, tmp_pkg_file + character(:), allocatable :: cache_path, target_url, tmp_file type(version_t) :: version integer :: stat, unit type(json_object) :: json @@ -666,18 +667,15 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade end if end if - ! Define location of the temporary folder and file. - tmp_pkg_path = join_path(global_settings%path_to_config_folder, 'tmp') - if (.not. exists(tmp_pkg_path)) call mkdir(tmp_pkg_path) - tmp_pkg_file = join_path(tmp_pkg_path, 'package_data.tmp') - open (newunit=unit, file=tmp_pkg_file, action='readwrite', iostat=stat) + tmp_file = get_temp_filename() + open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if ! Include namespace and package name in the target url and download package data. target_url = global_settings%registry_settings%url//'/packages/'//self%namespace//'/'//self%name - call downloader%get_pkg_data(target_url, self%requested_version, tmp_pkg_file, json, error) + call downloader%get_pkg_data(target_url, self%requested_version, tmp_file, json, error) close (unit, status='delete') if (allocated(error)) return @@ -686,7 +684,7 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (allocated(error)) return ! Open new tmp file for downloading the actual package. - open (newunit=unit, file=tmp_pkg_file, action='readwrite', iostat=stat) + open (newunit=unit, file=tmp_file, action='readwrite', iostat=stat) if (stat /= 0) then call fatal_error(error, "Error creating temporary file for downloading package '"//self%name//"'."); return end if @@ -697,13 +695,13 @@ subroutine get_from_registry(self, target_dir, global_settings, error, downloade if (is_dir(cache_path)) call os_delete_dir(os_is_unix(), cache_path) call mkdir(cache_path) - call downloader%get_file(target_url, tmp_pkg_file, error) + call downloader%get_file(target_url, tmp_file, error) if (allocated(error)) then close (unit, status='delete'); return end if ! Unpack the downloaded package to the final location. - call downloader%unpack(tmp_pkg_file, cache_path, error) + call downloader%unpack(tmp_file, cache_path, error) close (unit, status='delete') if (allocated(error)) return end if From c350228a60614fa62dad869a55e23f480a0e14a9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 01:39:56 -0500 Subject: [PATCH 505/799] Address reviews --- src/fpm/manifest/build.f90 | 1 - src/fpm/manifest/dependency.f90 | 5 ++--- src/fpm/manifest/meta.f90 | 2 +- test/fpm_test/test_manifest.f90 | 8 ++++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index fb7fae4c42..8047dd045d 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -34,7 +34,6 @@ module fpm_manifest_build logical :: module_naming = .false. type(string_t) :: module_prefix - !> Libraries to link against !> Libraries to link against type(string_t), allocatable :: link(:) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 9612f49e37..b770721a21 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -230,7 +230,7 @@ subroutine new_dependencies(deps, table, root, meta, error) ! An empty table is okay if (size(list) < 1) return - !> If requesting metapackages, do not stop on meta keywords + !> Count non-metapackage dependencies, and parse metapackage config if (present(meta)) then ndep = 0 do idep = 1, size(list) @@ -241,16 +241,15 @@ subroutine new_dependencies(deps, table, root, meta, error) !> Return metapackages config from this node call new_meta_config(meta, table, error) if (allocated(error)) return - else ndep = size(list) end if + ! Generate non-metapackage dependencies allocate(deps(ndep)) ndep = 0 do idep = 1, size(list) - ! Skip meta packages if (present(meta) .and. is_meta_package(list(idep)%key)) cycle ndep = ndep+1 diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index 9016932b7c..5cfb48c342 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -62,7 +62,7 @@ elemental subroutine request_destroy(self) end subroutine request_destroy - !> Parse version string of a metapackage reques + !> Parse version string of a metapackage request subroutine request_parse(self, version_request, error) ! Instance of this metapackage diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 14f39991f7..4c147e945d 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1367,7 +1367,7 @@ subroutine test_macro_parsing_dependency(error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: macrosPackage, macrosDependency + character(len=:), allocatable :: macros_package, macros_pependency type(package_config_t) :: package, dependency @@ -1413,10 +1413,10 @@ subroutine test_macro_parsing_dependency(error) pkg_ver = package%version%s() dep_ver = dependency%version%s() - macrosPackage = get_macros(id, package%preprocess(1)%macros, pkg_ver) - macrosDependency = get_macros(id, dependency%preprocess(1)%macros, dep_ver) + macros_package = get_macros(id, package%preprocess(1)%macros, pkg_ver) + macros_pependency = get_macros(id, dependency%preprocess(1)%macros, dep_ver) - if (macrosPackage == macrosDependency) then + if (macros_package == macros_pependency) then call test_failed(error, "Macros of package and dependency should not be equal") end if From 0b4ad660b7d8740abe5215ca3a3c21f9e88dd3e8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 01:48:47 -0500 Subject: [PATCH 506/799] merging fix --- src/fpm/manifest/package.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index d5b5e641fa..6c8fed4bb0 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -43,7 +43,7 @@ module fpm_manifest_package use fpm_manifest_library, only : library_config_t, new_library use fpm_manifest_install, only: install_config_t, new_install_config use fpm_manifest_test, only : test_config_t, new_test - use fpm_mainfest_preprocess, only : preprocess_config_t, new_preprocessors + use fpm_manifest_preprocess, only : preprocess_config_t, new_preprocessors use fpm_manifest_metapackages, only: metapackage_config_t, new_meta_config use fpm_filesystem, only : exists, getline, join_path use fpm_error, only : error_t, fatal_error, syntax_error, bad_name_error From 2ebb568e2aa1e3fc8a033dd7eeb0b894737cc572 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 01:57:01 -0500 Subject: [PATCH 507/799] (macOS) only install gfortran if not already available --- .github/workflows/meta.yml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 53fcd1c2e1..2771be9d99 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -34,7 +34,7 @@ jobs: mpi: openmpi - os: ubuntu-latest mpi: mpich - - os: windows-latest + - os: windows-latest mpi: msmpi # - os: windows-latest # mpi: intel @@ -51,7 +51,7 @@ jobs: if: contains(matrix.os,'ubuntu') run: | echo "GCC_V=10" >> $GITHUB_ENV - + - name: (macOS) setup gcc version if: contains(matrix.os,'macos') run: | @@ -59,7 +59,7 @@ jobs: - name: (Windows) Install MSYS2 uses: msys2/setup-msys2@v2 - if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') with: msystem: MINGW64 update: true @@ -70,22 +70,22 @@ jobs: unzip curl gcc-fortran - - - name: (Windows) Setup VS Build environment + + - name: (Windows) Setup VS Build environment if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') - uses: seanmiddleditch/gha-setup-vsdevenv@master + uses: seanmiddleditch/gha-setup-vsdevenv@master - name: (Windows) Retrieve Intel toolchain if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') shell: pwsh working-directory: C:\TEMP run: | - curl.exe --output webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 + curl.exe --output webimage.exe --url https://registrationcenter-download.intel.com/akdlm/irc_nas/19085/w_HPCKit_p_2023.0.0.25931_offline.exe --retry 5 --retry-delay 5 Start-Process -FilePath "webimage.exe" -ArgumentList "-s -x -f oneAPI --log extract.log" -Wait Remove-Item "webimage.exe" -Force Start-Process -FilePath "oneAPI\bootstrapper.exe" -ArgumentList "-s --action install --eula=accept --components=""intel.oneapi.win.cpp-compiler:intel.oneapi.win.ifort-compiler:intel.oneapi.win.mpi.devel"" -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." -Wait Remove-Item "oneAPI" -Force -Recurse - + - name: (Ubuntu) Install gfortran if: contains(matrix.os,'ubuntu') && (!contains(matrix.mpi,'intel')) run: | @@ -135,7 +135,7 @@ jobs: run: curl -L -O https://download.microsoft.com/download/a/5/2/a5207ca5-1203-491a-8fb8-906fd68ae623/msmpisetup.exe # 10.1.2 - name: (Windows) Install mpiexec.exe (-force needed to bypass GUI on headless) - if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') + if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') run: .\msmpisetup.exe -unattend -force - name: (Windows) test that mpiexec.exe exists @@ -145,7 +145,7 @@ jobs: - name: (Windows) test that OneAPI is installed if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') - run: | + run: | Test-Path -Path "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" -PathType leaf Test-Path -Path "C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat" -PathType leaf @@ -153,14 +153,14 @@ jobs: if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') run: | echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append + echo "MSMPI_BIN=C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_ENV -Append - - name: (Windows) load OneAPI environment variables + - name: (Windows) load OneAPI environment variables if: contains(matrix.os,'windows') && contains(matrix.mpi,'intel') shell: cmd run: | "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - "C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat" + "C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat" - name: (Windows) Install MSYS2 msmpi package if: contains(matrix.os,'windows') && contains(matrix.mpi,'msmpi') @@ -175,10 +175,11 @@ jobs: - name: (macOS) Install Homebrew gfortran if: contains(matrix.os, 'macos') run: | - brew install gcc@${{ env.GCC_V }} + # Only install gcc if not already available + which gfortran-${{ env.GCC_V }} || brew install gcc@${{ env.GCC_V }} which gfortran-${{ env.GCC_V }} which gfortran - # Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm + # Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm # have these paths hardcoded in the executable (no PIC?). Current bootstrap version 0.8.0 has gcc-10 mkdir /usr/local/opt/gcc@10 mkdir /usr/local/opt/gcc@10/lib @@ -220,7 +221,7 @@ jobs: echo "FPM_CXX=icpc" >> $GITHUB_ENV - name: (macOS) Use gcc/g++ instead of Clang for C/C++ - if: contains(matrix.os,'macOS') + if: contains(matrix.os,'macOS') shell: bash run: | echo "FPM_FC=gfortran-${{ env.GCC_V }}" >> $GITHUB_ENV From 69f06508848c2b82c1617cd368646f07a4bc98a6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 02:06:23 -0500 Subject: [PATCH 508/799] use libgcc_s.1.1.dylib if libgcc_s.1.dylib not available (gcc>=13) --- .github/workflows/meta.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 2771be9d99..8af7403f88 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -188,7 +188,9 @@ jobs: mkdir /usr/local/lib/gcc/10 ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib - ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib + # Newer gcc versions use libgcc_s.1.1.dylib + ls /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib && ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib + ls /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib && ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From f3b76a4b2b45ba7ab55a8a18385722be17977cf1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 02:11:03 -0500 Subject: [PATCH 509/799] Update meta.yml --- .github/workflows/meta.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 8af7403f88..6b16724ba5 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -189,8 +189,7 @@ jobs: ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib # Newer gcc versions use libgcc_s.1.1.dylib - ls /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib && ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - ls /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib && ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib + ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From 2f497aae9170a1aa7e61ce486a548fc21b57e9fd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 02:17:22 -0500 Subject: [PATCH 510/799] macOS test --- .github/workflows/meta.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 6b16724ba5..70520ff637 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,20 +28,20 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest - mpi: intel - - os: ubuntu-latest - mpi: openmpi - - os: ubuntu-latest - mpi: mpich - - os: windows-latest - mpi: msmpi +# - os: ubuntu-latest +# mpi: intel +# - os: ubuntu-latest +# mpi: openmpi +# - os: ubuntu-latest +# mpi: mpich +# - os: windows-latest +# mpi: msmpi # - os: windows-latest # mpi: intel - os: macos-latest mpi: openmpi - - os: macos-latest - mpi: mpich +# - os: macos-latest +# mpi: mpich steps: - name: Checkout code @@ -186,10 +186,10 @@ jobs: mkdir /usr/local/opt/gcc@10/lib/gcc mkdir /usr/local/opt/gcc@10/lib/gcc/10 mkdir /usr/local/lib/gcc/10 - ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib - ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib - # Newer gcc versions use libgcc_s.1.1.dylib - ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib +# ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib +# ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib +# # Newer gcc versions use libgcc_s.1.1.dylib +# ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From 48551925451317425ca8aa21f0b40fc0fa440635 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 02:19:01 -0500 Subject: [PATCH 511/799] Update meta.yml --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 70520ff637..afd151853d 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -178,7 +178,7 @@ jobs: # Only install gcc if not already available which gfortran-${{ env.GCC_V }} || brew install gcc@${{ env.GCC_V }} which gfortran-${{ env.GCC_V }} - which gfortran +# which gfortran # Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm # have these paths hardcoded in the executable (no PIC?). Current bootstrap version 0.8.0 has gcc-10 mkdir /usr/local/opt/gcc@10 From 5d664a411229c0832eb9e512016a57fbc678e926 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 02:21:14 -0500 Subject: [PATCH 512/799] Update meta.yml --- .github/workflows/meta.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index afd151853d..c6f057ec0b 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -178,7 +178,7 @@ jobs: # Only install gcc if not already available which gfortran-${{ env.GCC_V }} || brew install gcc@${{ env.GCC_V }} which gfortran-${{ env.GCC_V }} -# which gfortran + # which gfortran # Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm # have these paths hardcoded in the executable (no PIC?). Current bootstrap version 0.8.0 has gcc-10 mkdir /usr/local/opt/gcc@10 @@ -186,10 +186,10 @@ jobs: mkdir /usr/local/opt/gcc@10/lib/gcc mkdir /usr/local/opt/gcc@10/lib/gcc/10 mkdir /usr/local/lib/gcc/10 -# ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib -# ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib -# # Newer gcc versions use libgcc_s.1.1.dylib -# ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib + #ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib + #ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib + ## Newer gcc versions use libgcc_s.1.1.dylib + #ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From 2f1e3a9a8fd454b2ae6c09112223f835c9dc70c1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 02:23:49 -0500 Subject: [PATCH 513/799] fix link to gfortran --- .github/workflows/meta.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index c6f057ec0b..139aedf7fc 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -178,7 +178,7 @@ jobs: # Only install gcc if not already available which gfortran-${{ env.GCC_V }} || brew install gcc@${{ env.GCC_V }} which gfortran-${{ env.GCC_V }} - # which gfortran + which gfortran || ln -s /usr/local/bin/gfortran-${{ env.GCC_V }} /usr/local/bin/gfortran # Backport gfortran shared libraries to version 10 folder. This is necessary because all macOS releases of fpm # have these paths hardcoded in the executable (no PIC?). Current bootstrap version 0.8.0 has gcc-10 mkdir /usr/local/opt/gcc@10 @@ -186,10 +186,10 @@ jobs: mkdir /usr/local/opt/gcc@10/lib/gcc mkdir /usr/local/opt/gcc@10/lib/gcc/10 mkdir /usr/local/lib/gcc/10 - #ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib - #ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib - ## Newer gcc versions use libgcc_s.1.1.dylib - #ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib + ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libquadmath.0.dylib /usr/local/opt/gcc@10/lib/gcc/10/libquadmath.0.dylib + ln -fs /usr/local/opt/gcc@${{ env.GCC_V }}/lib/gcc/${{ env.GCC_V }}/libgfortran.5.dylib /usr/local/opt/gcc@10/lib/gcc/10/libgfortran.5.dylib + # Newer gcc versions use libgcc_s.1.1.dylib + ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib || ln -fs /usr/local/lib/gcc/${{ env.GCC_V }}/libgcc_s.1.1.dylib /usr/local/lib/gcc/10/libgcc_s.1.dylib - name: (macOS) Install homebrew MPICH if: contains(matrix.mpi,'mpich') && contains(matrix.os,'macos') From 02adf0f20a4429f562f53c3d3cae0e1eac63315d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 07:57:10 -0500 Subject: [PATCH 514/799] restore all environments --- .github/workflows/meta.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 139aedf7fc..b32bf0c90c 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,20 +28,20 @@ jobs: fail-fast: false matrix: include: -# - os: ubuntu-latest -# mpi: intel -# - os: ubuntu-latest -# mpi: openmpi -# - os: ubuntu-latest -# mpi: mpich -# - os: windows-latest -# mpi: msmpi + - os: ubuntu-latest + mpi: intel + - os: ubuntu-latest + mpi: openmpi + - os: ubuntu-latest + mpi: mpich + - os: windows-latest + mpi: msmpi # - os: windows-latest # mpi: intel - os: macos-latest mpi: openmpi -# - os: macos-latest -# mpi: mpich + - os: macos-latest + mpi: mpich steps: - name: Checkout code From ce8334ab9520d724cb610bbed11a549e2b829697 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 08:13:19 -0500 Subject: [PATCH 515/799] test windows only --- .github/workflows/meta.yml | 21 +++++++++++---------- src/fpm_meta.f90 | 8 ++++++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index b32bf0c90c..0266ef2b78 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,20 +28,21 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest - mpi: intel - - os: ubuntu-latest - mpi: openmpi - - os: ubuntu-latest - mpi: mpich + # - os: ubuntu-latest + # mpi: intel + # - os: ubuntu-latest + # mpi: openmpi + # - os: ubuntu-latest + # mpi: mpich - os: windows-latest mpi: msmpi # - os: windows-latest # mpi: intel - - os: macos-latest - mpi: openmpi - - os: macos-latest - mpi: mpich + # - os: macos-latest + # mpi: openmpi + # - os: macos-latest + # mpi: mpich + steps: - name: Checkout code diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 0faa916d19..6764bb826b 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -577,17 +577,21 @@ logical function msmpi_init(this,compiler,error) result(found) ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). ! Do a second attempt: search for mpiexec.exe if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then + print *, '+ MSMPI_BIN path does not exist, searching mpiexec.exe....' call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) endif ! Do a third attempt: search for mpiexec.exe in the default location if (len_trim(bindir)<=0 .or. .not.exists(bindir) .or. allocated(error)) then + print *, '+ MSMPI_BIN path does not exist, searching C:\Program Files\Microsoft MPI\Bin\mpiexec.exe....' windir = get_dos_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',error) print *, 'windir=',windir - if (.not.allocated(error)) & - call find_command_location(windir,bindir,verbose=verbose,error=error) + if (.not.allocated(error)) then + print *, '+ searching location of ',windir + call find_command_location(windir,bindir,verbose=verbose,error=error) + endif endif From 1db25494af09d0ba21cfc683363f73ff4cb5e2e5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 08:20:05 -0500 Subject: [PATCH 516/799] increase line buffer length to Windows max --- src/fpm_filesystem.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index d2ffb61f0c..7367dcbbd0 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -16,7 +16,7 @@ module fpm_filesystem filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, get_tmp_directory, & execute_and_read_output, get_dos_path - integer, parameter :: LINE_BUFFER_LEN = 1000 + integer, parameter :: LINE_BUFFER_LEN = 32768 #ifndef FPM_BOOTSTRAP interface From 9977834a3e4c631a47e50a18fa2861bf68922af0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 08:31:58 -0500 Subject: [PATCH 517/799] restore all environments --- .github/workflows/meta.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 0266ef2b78..93075fd812 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -28,20 +28,18 @@ jobs: fail-fast: false matrix: include: - # - os: ubuntu-latest - # mpi: intel - # - os: ubuntu-latest - # mpi: openmpi - # - os: ubuntu-latest - # mpi: mpich + - os: ubuntu-latest + mpi: intel + - os: ubuntu-latest + mpi: openmpi + - os: ubuntu-latest + mpi: mpich - os: windows-latest mpi: msmpi - # - os: windows-latest - # mpi: intel - # - os: macos-latest - # mpi: openmpi - # - os: macos-latest - # mpi: mpich + - os: macos-latest + mpi: openmpi + - os: macos-latest + mpi: mpich steps: From 6ea99998e0ddf8c92212a839607cbbfc00ceba9a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 17:17:33 +0200 Subject: [PATCH 518/799] bump version to 0.8.2 --- fpm.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index dcd3f27743..4aff58773c 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,9 +1,9 @@ name = "fpm" -version = "0.8.1" +version = "0.8.2" license = "MIT" author = "fpm maintainers" maintainer = "" -copyright = "2020 fpm contributors" +copyright = "2020-2023 fpm contributors" [preprocess] [preprocess.cpp] From 79373b97423c03346fce9fcf61fbb16121ebac70 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 18:43:22 +0200 Subject: [PATCH 519/799] MS_MPI: make DOS path --- src/fpm_meta.f90 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 6764bb826b..cfa3e8ebb5 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -572,11 +572,16 @@ logical function msmpi_init(this,compiler,error) result(found) ! Check that the runtime is installed bindir = get_env('MSMPI_BIN') + ! Always use DOS paths with no spaces + if (len_trim(bindir)>0) then + bindir = get_dos_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',error) + endif + print *, 'bindir=',bindir ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). ! Do a second attempt: search for mpiexec.exe - if (len_trim(bindir)<=0 .or. .not.exists(bindir)) then + if (len_trim(bindir)<=0 .or. .not.exists(bindir) .or. allocated(error)) then print *, '+ MSMPI_BIN path does not exist, searching mpiexec.exe....' call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) endif From 7dca2e86eb9666bf3956659a856563df578d153f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 19:06:41 +0200 Subject: [PATCH 520/799] add many checks --- src/fpm_meta.f90 | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index cfa3e8ebb5..6e5e87c8fb 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -736,12 +736,18 @@ subroutine find_command_location(command,path,echo,verbose,error) return end if + print *, '+ get temp filename...' + tmp_file = get_temp_filename() + print *, '+ get temp filename... '//tmp_file + ! On Windows, we try both commands because we may be on WSL do try=merge(1,2,get_os_type()==OS_WINDOWS),2 search_command = search(try)//command + print *, '+ attempt ',try,': ',search_command call run(search_command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) + print *, 'after run, stat=',stat if (stat==0) exit end do if (stat/=0) then @@ -755,6 +761,7 @@ subroutine find_command_location(command,path,echo,verbose,error) if (stat == 0)then do call getline(iunit, line, stat) + print *, 'get line, stat=',stat if (stat /= 0) exit if (len(screen_output)>0) then screen_output = screen_output//new_line('a')//line @@ -771,6 +778,8 @@ subroutine find_command_location(command,path,echo,verbose,error) ! Only use the first instance length = index(screen_output,new_line('a')) + + print *, '+ get line length: ',length multiline: if (length>1) then fullpath = screen_output(1:length-1) else @@ -783,6 +792,7 @@ subroutine find_command_location(command,path,echo,verbose,error) ! Extract path only length = index(fullpath,command,BACK=.true.) + print *, 'extract fullpath, length=',length if (length<=0) then call fatal_error(error,'full path to command ('//command//') does not include command name') return @@ -795,9 +805,13 @@ subroutine find_command_location(command,path,echo,verbose,error) if (allocated(error)) return ! On Windows, be sure to return a path with no spaces - if (get_os_type()==OS_WINDOWS) path = get_dos_path(path,error) + if (get_os_type()==OS_WINDOWS) then + print *, 'get dos path' + path = get_dos_path(path,error) + print *, 'dos path = ',path + end if - if (.not.is_dir(path)) then + if (allocated(error) .or. .not.is_dir(path)) then call fatal_error(error,'full path ('//path//') to command ('//command//') is not a directory') return end if From 0fcca0ebc4fc1b53834d45d47c456d28e5797e32 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 20:05:27 +0200 Subject: [PATCH 521/799] echo runner folder --- src/fpm_meta.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 6e5e87c8fb..af4ea39fd7 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -836,6 +836,7 @@ subroutine get_mpi_runner(command,verbose,error) ! Success! success = len_trim(command%s)>0 if (success) then + if (verbose) print *, '+ runner folder found: '//command%s command%s = join_path(command%s,trim(try(itri))) return endif From 1a948beeabc5852e8ec56d11f9c702033ddafc57 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 20:26:19 +0200 Subject: [PATCH 522/799] refactor windows search --- src/fpm_meta.f90 | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index af4ea39fd7..8fbc069217 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -570,31 +570,26 @@ logical function msmpi_init(this,compiler,error) result(found) end if ! Check that the runtime is installed - bindir = get_env('MSMPI_BIN') + bindir = "" + call get_absolute_path(get_env('MSMPI_BIN'),bindir,error) - ! Always use DOS paths with no spaces - if (len_trim(bindir)>0) then - bindir = get_dos_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',error) - endif - - print *, 'bindir=',bindir + print *, '+ bindir=',bindir + print *, '+ windir=',windir ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). - ! Do a second attempt: search for mpiexec.exe - if (len_trim(bindir)<=0 .or. .not.exists(bindir) .or. allocated(error)) then - print *, '+ MSMPI_BIN path does not exist, searching mpiexec.exe....' - call find_command_location('mpiexec.exe',bindir,verbose=verbose,error=error) + ! Do a second attempt: search for the default location + if (len_trim(bindir)<=0 .or. allocated(error)) then + print *, '+ MSMPI_BIN path does not exist, searching C:\Program Files\Microsoft MPI\Bin\....' + call get_absolute_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',bindir,error) endif - ! Do a third attempt: search for mpiexec.exe in the default location - if (len_trim(bindir)<=0 .or. .not.exists(bindir) .or. allocated(error)) then - print *, '+ MSMPI_BIN path does not exist, searching C:\Program Files\Microsoft MPI\Bin\mpiexec.exe....' - windir = get_dos_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',error) + ! Do a third attempt: search for mpiexec.exe in PATH location + if (len_trim(bindir)<=0 .or. allocated(error)) then - print *, 'windir=',windir + call get_mpi_runner(windir,verbose,error) if (.not.allocated(error)) then - print *, '+ searching location of ',windir + print *, '+ searching location of mpi runner, ',windir call find_command_location(windir,bindir,verbose=verbose,error=error) endif @@ -821,7 +816,7 @@ end subroutine find_command_location !> Get MPI runner in $PATH subroutine get_mpi_runner(command,verbose,error) type(string_t), intent(out) :: command - logical, optional, intent(in) :: verbose + logical, intent(in) :: verbose type(error_t), allocatable, intent(out) :: error character(*), parameter :: try(*) = ['mpiexec','mpirun '] From bad1556ecada542fe42bbd70aef12089f1569daf Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 20:27:05 +0200 Subject: [PATCH 523/799] Update fpm_meta.f90 --- src/fpm_meta.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 8fbc069217..a414ceb7ae 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -539,7 +539,7 @@ logical function msmpi_init(this,compiler,error) result(found) character(len=:), allocatable :: incdir,windir,libdir,bindir,post,reall,msysdir type(version_t) :: ver,ver10 - type(string_t) :: cpath,msys_path + type(string_t) :: cpath,msys_path,runner_path logical :: msys2 !> Default: not found @@ -586,11 +586,11 @@ logical function msmpi_init(this,compiler,error) result(found) ! Do a third attempt: search for mpiexec.exe in PATH location if (len_trim(bindir)<=0 .or. allocated(error)) then - call get_mpi_runner(windir,verbose,error) + call get_mpi_runner(runner_path,verbose,error) if (.not.allocated(error)) then print *, '+ searching location of mpi runner, ',windir - call find_command_location(windir,bindir,verbose=verbose,error=error) + call find_command_location(runner_path%s,bindir,verbose=verbose,error=error) endif endif From f8e13ee4005182ff5824dbcdfadaaa5604f755ce Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 11 May 2023 20:36:02 +0200 Subject: [PATCH 524/799] cleanup debugging prints --- src/fpm_meta.f90 | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index a414ceb7ae..438ceee5f0 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -94,7 +94,7 @@ module fpm_meta public :: MPI_TYPE_NAME !> Debugging information -logical, parameter, private :: verbose = .true. +logical, parameter, private :: verbose = .false. integer, parameter, private :: LANG_FORTRAN = 1 integer, parameter, private :: LANG_C = 2 @@ -572,24 +572,23 @@ logical function msmpi_init(this,compiler,error) result(found) ! Check that the runtime is installed bindir = "" call get_absolute_path(get_env('MSMPI_BIN'),bindir,error) - - print *, '+ bindir=',bindir - print *, '+ windir=',windir + if (verbose) print *, '+ %MSMPI_BIN%=',bindir ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). ! Do a second attempt: search for the default location if (len_trim(bindir)<=0 .or. allocated(error)) then - print *, '+ MSMPI_BIN path does not exist, searching C:\Program Files\Microsoft MPI\Bin\....' + if (verbose) print *, '+ %MSMPI_BIN% empty, searching C:\Program Files\Microsoft MPI\Bin\ ...' call get_absolute_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',bindir,error) endif ! Do a third attempt: search for mpiexec.exe in PATH location if (len_trim(bindir)<=0 .or. allocated(error)) then + if (verbose) print *, '+ C:\Program Files\Microsoft MPI\Bin\ not found. searching %PATH%...' call get_mpi_runner(runner_path,verbose,error) if (.not.allocated(error)) then - print *, '+ searching location of mpi runner, ',windir + if (verbose) print *, '+ mpiexec found: ',runner_path%s call find_command_location(runner_path%s,bindir,verbose=verbose,error=error) endif @@ -731,18 +730,12 @@ subroutine find_command_location(command,path,echo,verbose,error) return end if - print *, '+ get temp filename...' - tmp_file = get_temp_filename() - print *, '+ get temp filename... '//tmp_file - ! On Windows, we try both commands because we may be on WSL do try=merge(1,2,get_os_type()==OS_WINDOWS),2 search_command = search(try)//command - print *, '+ attempt ',try,': ',search_command call run(search_command, echo=echo, exitstat=stat, verbose=verbose, redirect=tmp_file) - print *, 'after run, stat=',stat if (stat==0) exit end do if (stat/=0) then @@ -756,7 +749,6 @@ subroutine find_command_location(command,path,echo,verbose,error) if (stat == 0)then do call getline(iunit, line, stat) - print *, 'get line, stat=',stat if (stat /= 0) exit if (len(screen_output)>0) then screen_output = screen_output//new_line('a')//line @@ -774,7 +766,6 @@ subroutine find_command_location(command,path,echo,verbose,error) ! Only use the first instance length = index(screen_output,new_line('a')) - print *, '+ get line length: ',length multiline: if (length>1) then fullpath = screen_output(1:length-1) else @@ -787,7 +778,6 @@ subroutine find_command_location(command,path,echo,verbose,error) ! Extract path only length = index(fullpath,command,BACK=.true.) - print *, 'extract fullpath, length=',length if (length<=0) then call fatal_error(error,'full path to command ('//command//') does not include command name') return @@ -800,11 +790,7 @@ subroutine find_command_location(command,path,echo,verbose,error) if (allocated(error)) return ! On Windows, be sure to return a path with no spaces - if (get_os_type()==OS_WINDOWS) then - print *, 'get dos path' - path = get_dos_path(path,error) - print *, 'dos path = ',path - end if + if (get_os_type()==OS_WINDOWS) path = get_dos_path(path,error) if (allocated(error) .or. .not.is_dir(path)) then call fatal_error(error,'full path ('//path//') to command ('//command//') is not a directory') @@ -1195,7 +1181,7 @@ subroutine run_mpi_wrapper(wrapper,args,verbose,exitcode,cmd_success,screen_outp ! Empty command if (len_trim(wrapper)<=0) then - if (verbose) print *, '+ ' + if (echo_local) print *, '+ ' if (present(exitcode)) exitcode = 0 if (present(cmd_success)) cmd_success = .true. if (present(screen_output)) screen_output = string_t("") From 5a1656440089b1a7510517bb7807245707240665 Mon Sep 17 00:00:00 2001 From: gnikit Date: Sun, 14 May 2023 22:13:16 +0100 Subject: [PATCH 525/799] build: changed file ext to enable preprocessor (#911) lowercase file extension was preventing the copmiler (gfortran) to identify the existence of preprocessor definitions in the file and turn on preproc parsing. This caused issues downstream, during the creation of fpm PyPi wheels. Fixes #910 --- src/fpm/{fpm_release.f90 => fpm_release.F90} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/fpm/{fpm_release.f90 => fpm_release.F90} (100%) diff --git a/src/fpm/fpm_release.f90 b/src/fpm/fpm_release.F90 similarity index 100% rename from src/fpm/fpm_release.f90 rename to src/fpm/fpm_release.F90 From 9ff449514d997965dd645439100ddce995c7002f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 15 May 2023 20:13:30 +0700 Subject: [PATCH 526/799] Use get_tmp_filename --- src/fpm/cmd/publish.f90 | 13 ++++++------ src/fpm/git.f90 | 8 ++------ src/fpm_filesystem.F90 | 45 ++++++----------------------------------- 3 files changed, 14 insertions(+), 52 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 09fc465272..dc83880f14 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -8,8 +8,8 @@ module fpm_cmd_publish use fpm_model, only: fpm_model_t use fpm_error, only: error_t, fpm_stop use fpm_versioning, only: version_t - use fpm_filesystem, only: exists, join_path, get_tmp_directory - use fpm_git, only: git_archive, compressed_package_name + use fpm_filesystem, only: exists, join_path, get_temp_filename + use fpm_git, only: git_archive use fpm_downloader, only: downloader_t use fpm_strings, only: string_t use fpm_settings, only: official_registry_base_url @@ -31,7 +31,7 @@ subroutine cmd_publish(settings) type(error_t), allocatable :: error type(version_t), allocatable :: version type(string_t), allocatable :: form_data(:) - character(len=:), allocatable :: tmpdir + character(len=:), allocatable :: tmp_file type(downloader_t) :: downloader integer :: i @@ -69,11 +69,10 @@ subroutine cmd_publish(settings) if (allocated(settings%token)) form_data = [form_data, string_t('upload_token="'//settings%token//'"')] - call get_tmp_directory(tmpdir, error) - if (allocated(error)) call fpm_stop(1, '*cmd_publish* Tmp directory error: '//error%message) - call git_archive('.', tmpdir, error) + tmp_file = get_temp_filename() + call git_archive('.', tmp_file, error) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Pack error: '//error%message) - form_data = [form_data, string_t('tarball=@"'//join_path(tmpdir, compressed_package_name)//'"')] + form_data = [form_data, string_t('tarball=@"'//tmp_file//'"')] if (settings%show_form_data) then do i = 1, size(form_data) diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index be4b99bcf6..602516ea74 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -5,10 +5,7 @@ module fpm_git implicit none public :: git_target_t, git_target_default, git_target_branch, git_target_tag, git_target_revision, git_revision, & - & git_archive, git_matches_manifest, operator(==), compressed_package_name - - !> Name of the compressed package that is generated temporarily. - character(len=*), parameter :: compressed_package_name = 'compressed_package' + & git_archive, git_matches_manifest, operator(==) !> Possible git target type :: enum_descriptor @@ -326,8 +323,7 @@ subroutine git_archive(source, destination, error) call fatal_error(error, "Cannot find a suitable archive format for 'git archive'."); return end if - call execute_command_line('git archive HEAD --format='//archive_format//' -o '// & - & join_path(destination, compressed_package_name), exitstat=stat) + call execute_command_line('git archive HEAD --format='//archive_format//' -o '// destination, exitstat=stat) if (stat /= 0) then call fatal_error(error, "Error packing '"//source//"'."); return end if diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index c7b12a8b5e..4cfe571b6f 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -14,8 +14,7 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, get_tmp_directory, & - execute_and_read_output + LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, execute_and_read_output integer, parameter :: LINE_BUFFER_LEN = 1000 #ifndef FPM_BOOTSTRAP @@ -1033,21 +1032,15 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) integer, intent(out), optional :: exitstat integer :: cmdstat, unit, stat = 0 - character(len=:), allocatable :: cmdmsg, tmp_path + character(len=:), allocatable :: cmdmsg, tmp_file character(len=1000) :: output_line - call get_tmp_directory(tmp_path, error) - if (allocated(error)) return + tmp_file = get_temp_filename() - if (.not. exists(tmp_path)) call mkdir(tmp_path) - tmp_path = join_path(tmp_path, 'command_line_output') - call delete_file(tmp_path) - call filewrite(tmp_path, ['']) + call execute_command_line(cmd//' > '//tmp_file, exitstat=exitstat, cmdstat=cmdstat) + if (cmdstat /= 0) call fatal_error(error, '*run*: '//"Command failed: '"//cmd//"'. Message: '"//trim(cmdmsg)//"'.") - call execute_command_line(cmd//' > '//tmp_path, exitstat=exitstat, cmdstat=cmdstat) - if (cmdstat /= 0) call fpm_stop(1,'*run*: '//"Command failed: '"//cmd//"'. Message: '"//trim(cmdmsg)//"'.") - - open(unit, file=tmp_path, action='read', status='old') + open(newunit=unit, file=tmp_file, action='read', status='old') output = '' do read(unit, *, iostat=stat) output_line @@ -1056,30 +1049,4 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) end do close(unit, status='delete') end - - !> Get system-dependent tmp directory. - subroutine get_tmp_directory(tmp_dir, error) - !> System-dependant tmp directory. - character(len=:), allocatable, intent(out) :: tmp_dir - !> Error to handle. - type(error_t), allocatable, intent(out) :: error - - tmp_dir = get_env('TMPDIR', '') - if (tmp_dir /= '') then - tmp_dir = tmp_dir//'fpm'; return - end if - - tmp_dir = get_env('TMP', '') - if (tmp_dir /= '') then - tmp_dir = tmp_dir//'fpm'; return - end if - - tmp_dir = get_env('TEMP', '') - if (tmp_dir /= '') then - tmp_dir = tmp_dir//'fpm'; return - end if - - call fatal_error(error, "Couldn't determine system temporary directory.") - end - end module fpm_filesystem From 6abbde187fc7028a3e5e69061e764658942338a3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 15 May 2023 11:27:44 -0500 Subject: [PATCH 527/799] fallback to 0.8.0 if install.sh fails to fetch github --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 2edc239508..4243ba6266 100644 --- a/install.sh +++ b/install.sh @@ -73,9 +73,9 @@ fi LATEST_RELEASE=$(get_latest_release "fortran-lang/fpm" "$FETCH") +# Fallback to a latest known release if network timeout if [ -z "$LATEST_RELEASE" ]; then - echo "Could not fetch the latest release from GitHub. Install curl or wget, and ensure network connectivity." - exit 3 + LATEST_RELEASE="0.8.0" fi SOURCE_URL="https://github.com/fortran-lang/fpm/releases/download/v${LATEST_RELEASE}/fpm-${LATEST_RELEASE}.F90" From 3e53f8547383684e8fd65f6bfc0280c3b6361af1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 13:28:08 +0200 Subject: [PATCH 528/799] `build_config_t`: make `serializable_t` and test --- src/fpm/manifest/build.f90 | 126 ++++++++++++++++++++++++++++++-- test/fpm_test/test_manifest.f90 | 10 +++ 2 files changed, 130 insertions(+), 6 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 537fd3dd3a..4c743927a9 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -11,24 +11,25 @@ !>``` module fpm_manifest_build use fpm_error, only : error_t, syntax_error, fatal_error - use fpm_strings, only : string_t, len_trim, is_valid_module_prefix - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list + use fpm_strings, only : string_t, len_trim, is_valid_module_prefix, operator(==) + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list, serializable_t, & + set_value, set_string, set_list implicit none private public :: build_config_t, new_build_config !> Configuration data for build - type :: build_config_t + type, extends(serializable_t) :: build_config_t !> Automatic discovery of executables - logical :: auto_executables + logical :: auto_executables = .true. !> Automatic discovery of examples - logical :: auto_examples + logical :: auto_examples = .true. !> Automatic discovery of tests - logical :: auto_tests + logical :: auto_tests = .true. !> Enforcing of package module names logical :: module_naming = .false. @@ -45,8 +46,15 @@ module fpm_manifest_build !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => build_conf_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type build_config_t + character(*), parameter, private :: class_name = 'build_config_t' + contains @@ -211,4 +219,110 @@ subroutine info(self, unit, verbosity) end subroutine info + !> Check that two dependency trees are equal + logical function build_conf_is_same(this,that) + class(build_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + build_conf_is_same = .false. + + select type (other=>that) + type is (build_config_t) + + if (this%auto_executables.neqv.other%auto_executables) return + if (this%auto_examples.neqv.other%auto_examples) return + if (this%auto_tests.neqv.other%auto_tests) return + if (this%module_naming.neqv.other%module_naming) return + if (.not.this%module_prefix==other%module_prefix) return + if (.not.this%link==other%link) return + if (.not.this%external_modules==other%external_modules) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + build_conf_is_same = .true. + + end function build_conf_is_same + + !> Dump build config to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(build_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call set_value(table, "auto-executables", self%auto_executables, error, class_name) + if (allocated(error)) return + call set_value(table, "auto-tests", self%auto_tests, error, class_name) + if (allocated(error)) return + call set_value(table, "auto-examples", self%auto_examples, error, class_name) + if (allocated(error)) return + + ! Module naming can either contain a boolean value, or the prefix + has_prefix: if (self%module_naming .and. len_trim(self%module_prefix)>0) then + call set_string(table, "module-naming", self%module_prefix, error, class_name) + else + call set_value (table, "module-naming", self%module_naming, error, class_name) + end if has_prefix + if (allocated(error)) return + + call set_list(table, "link", self%link, error) + if (allocated(error)) return + call set_list(table, "external-modules", self%external_modules, error) + if (allocated(error)) return + + end subroutine dump_to_toml + + !> Read build config from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(build_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + call get_value(table, "auto-executables", self%auto_executables, error, class_name) + if (allocated(error)) return + call get_value(table, "auto-tests", self%auto_tests, error, class_name) + if (allocated(error)) return + call get_value(table, "auto-examples", self%auto_examples, error, class_name) + if (allocated(error)) return + + !> Module naming: fist, attempt boolean value first + call get_value(table, "module-naming", self%module_naming, .false., stat=stat) + if (stat == toml_stat%success) then + ! Boolean value found. Set no custom prefix. This also falls back to key not provided + self%module_prefix = string_t("") + else + !> Value found, but not a boolean. Attempt to read a prefix string + call get_value(table, "module-naming", self%module_prefix%s) + if (.not.allocated(self%module_prefix%s)) then + call syntax_error(error,"Could not read value for 'module-naming' in fpm.toml, expecting logical or a string") + return + end if + self%module_naming = .true. + end if + + call get_list(table, "link", self%link, error) + if (allocated(error)) return + call get_list(table, "external-modules", self%external_modules, error) + if (allocated(error)) return + + end subroutine load_from_toml + + end module fpm_manifest_build diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index cd2605f4e3..917fd314fd 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -1206,6 +1206,11 @@ subroutine test_link_string(error) call set_value(table, "link", "z", stat=stat) call new_build_config(build, table, 'test_link_string', error) + if (allocated(error)) return + + !> Test serialization roundtrip + call build%test_serialization('test_link_string', error) + if (allocated(error)) return end subroutine test_link_string @@ -1229,6 +1234,11 @@ subroutine test_link_array(error) call set_value(children, 2, "lapack", stat=stat) call new_build_config(build, table, 'test_link_array', error) + if (allocated(error)) return + + !> Test serialization roundtrip + call build%test_serialization('test_link_string', error) + if (allocated(error)) return end subroutine test_link_array From fead5282f22965d9d6c3963417ec312427b8e6d9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 14:37:39 +0200 Subject: [PATCH 529/799] `new_build_config`: use standardized `load_from_toml` --- src/fpm/manifest/build.f90 | 56 +------------------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 4c743927a9..e1835d0639 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -79,61 +79,7 @@ subroutine new_build_config(self, table, package_name, error) call check(table, package_name, error) if (allocated(error)) return - call get_value(table, "auto-executables", self%auto_executables, .true., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'auto-executables' in fpm.toml, expecting logical") - return - end if - - call get_value(table, "auto-tests", self%auto_tests, .true., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'auto-tests' in fpm.toml, expecting logical") - return - end if - - call get_value(table, "auto-examples", self%auto_examples, .true., stat=stat) - - if (stat /= toml_stat%success) then - call fatal_error(error,"Error while reading value for 'auto-examples' in fpm.toml, expecting logical") - return - end if - - !> Module naming: fist, attempt boolean value first - call get_value(table, "module-naming", self%module_naming, .false., stat=stat) - - if (stat == toml_stat%success) then - - ! Boolean value found. Set no custom prefix. This also falls back to - ! key not provided - self%module_prefix = string_t("") - - else - - !> Value found, but not a boolean. Attempt to read a prefix string - call get_value(table, "module-naming", self%module_prefix%s) - - if (.not.allocated(self%module_prefix%s)) then - call syntax_error(error,"Could not read value for 'module-naming' in fpm.toml, expecting logical or a string") - return - end if - - if (.not.is_valid_module_prefix(self%module_prefix)) then - call syntax_error(error,"Invalid custom module name prefix for in fpm.toml: <"//self%module_prefix%s// & - ">, expecting a valid alphanumeric string") - return - end if - - ! Set module naming to ON - self%module_naming = .true. - - end if - - call get_list(table, "link", self%link, error) - if (allocated(error)) return - - call get_list(table, "external-modules", self%external_modules, error) + call self%load_from_toml(table, error) if (allocated(error)) return end subroutine new_build_config From 2eb9aee84f1cb50641448541c491f45d628a8b05 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 14:49:38 +0200 Subject: [PATCH 530/799] Revert "`new_build_config`: use standardized `load_from_toml`" This reverts commit fead5282f22965d9d6c3963417ec312427b8e6d9. --- src/fpm/manifest/build.f90 | 56 +++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index e1835d0639..4c743927a9 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -79,7 +79,61 @@ subroutine new_build_config(self, table, package_name, error) call check(table, package_name, error) if (allocated(error)) return - call self%load_from_toml(table, error) + call get_value(table, "auto-executables", self%auto_executables, .true., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'auto-executables' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "auto-tests", self%auto_tests, .true., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'auto-tests' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "auto-examples", self%auto_examples, .true., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'auto-examples' in fpm.toml, expecting logical") + return + end if + + !> Module naming: fist, attempt boolean value first + call get_value(table, "module-naming", self%module_naming, .false., stat=stat) + + if (stat == toml_stat%success) then + + ! Boolean value found. Set no custom prefix. This also falls back to + ! key not provided + self%module_prefix = string_t("") + + else + + !> Value found, but not a boolean. Attempt to read a prefix string + call get_value(table, "module-naming", self%module_prefix%s) + + if (.not.allocated(self%module_prefix%s)) then + call syntax_error(error,"Could not read value for 'module-naming' in fpm.toml, expecting logical or a string") + return + end if + + if (.not.is_valid_module_prefix(self%module_prefix)) then + call syntax_error(error,"Invalid custom module name prefix for in fpm.toml: <"//self%module_prefix%s// & + ">, expecting a valid alphanumeric string") + return + end if + + ! Set module naming to ON + self%module_naming = .true. + + end if + + call get_list(table, "link", self%link, error) + if (allocated(error)) return + + call get_list(table, "external-modules", self%external_modules, error) if (allocated(error)) return end subroutine new_build_config From b120b6a4813cc0617d47499780358a67a2b69a29 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 19:18:45 +0200 Subject: [PATCH 531/799] `install_config_t`: make serializable --- src/fpm/manifest/install.f90 | 66 ++++++++++++++++++++++++++++++++++-- test/fpm_test/test_toml.f90 | 24 +++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/fpm/manifest/install.f90 b/src/fpm/manifest/install.f90 index 6175873937..98c2ef5321 100644 --- a/src/fpm/manifest/install.f90 +++ b/src/fpm/manifest/install.f90 @@ -7,14 +7,14 @@ !>``` module fpm_manifest_install use fpm_error, only : error_t, fatal_error, syntax_error - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, set_value, serializable_t implicit none private public :: install_config_t, new_install_config !> Configuration data for installation - type :: install_config_t + type, extends(serializable_t) :: install_config_t !> Install library with this project logical :: library @@ -24,8 +24,16 @@ module fpm_manifest_install !> Print information on this instance procedure :: info + + !> Serialization interface + procedure :: serializable_is_same => install_conf_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type install_config_t + character(*), parameter :: class_name = 'install_config_t' + contains !> Create a new installation configuration from a TOML data structure @@ -105,4 +113,58 @@ subroutine info(self, unit, verbosity) end subroutine info + logical function install_conf_same(this,that) + class(install_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + install_conf_same = .false. + + select type (other=>that) + type is (install_config_t) + if (this%library.neqv.other%library) return + class default + ! Not the same type + return + end select + + !> All checks passed! + install_conf_same = .true. + + end function install_conf_same + + !> Dump install config to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(install_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call set_value(table, "library", self%library, error, class_name) + + end subroutine dump_to_toml + + !> Read install config from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(install_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: stat + + call get_value(table, "library", self%library, error, class_name) + if (allocated(error)) return + + end subroutine load_from_toml + end module fpm_manifest_install diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index fa17b2fea3..909e6a4deb 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -7,12 +7,14 @@ module test_toml use fpm_dependency, only: dependency_node_t, destroy_dependency_node, dependency_tree_t, & & new_dependency_node, new_dependency_tree, resize use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy + use fpm_manifest_install use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & & srcfile_t use fpm_compiler, only: archiver_t, compiler_t, id_gcc + implicit none private @@ -42,6 +44,7 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-dependency-tree", dependency_tree_roundtrip), & & new_unittest("serialize-dependency-tree-invalid", dependency_tree_invalid, should_fail=.true.), & & new_unittest("serialize-dependency-tree-invalid2", dependency_tree_invalid2, should_fail=.true.), & + & new_unittest("serialize-install-config", install_config_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-fortran-invalid", fft_invalid, should_fail=.true.), & @@ -1142,4 +1145,25 @@ subroutine fpm_model_invalid(error) end subroutine fpm_model_invalid + subroutine install_config_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(install_config_t) :: install + + integer :: loop + + do loop=1,2 + + install % library = mod(loop,2)==0 + + ! Test full object + call install%test_serialization('install_config_roundtrip',error) + if (allocated(error)) return + + end do + + end subroutine install_config_roundtrip + end module test_toml From 95cd89fa8f3816e252484a69b7ab26ee4fe735d8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 19:33:46 +0200 Subject: [PATCH 532/799] serialize `fortran_config_t` --- src/fpm/manifest/fortran.f90 | 82 ++++++++++++++++++++++++++++++++++-- src/fpm/manifest/install.f90 | 1 - test/fpm_test/test_toml.f90 | 23 ++++++++++ 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/src/fpm/manifest/fortran.f90 b/src/fpm/manifest/fortran.f90 index bf76fa2e38..231191c433 100644 --- a/src/fpm/manifest/fortran.f90 +++ b/src/fpm/manifest/fortran.f90 @@ -1,25 +1,35 @@ module fpm_manifest_fortran use fpm_error, only : error_t, syntax_error, fatal_error - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, set_string implicit none private public :: fortran_config_t, new_fortran_config !> Configuration data for Fortran - type :: fortran_config_t + type, extends(serializable_t) :: fortran_config_t !> Enable default implicit typing - logical :: implicit_typing + logical :: implicit_typing = .false. !> Enable implicit external interfaces - logical :: implicit_external + logical :: implicit_external = .false. !> Form to use for all Fortran sources character(:), allocatable :: source_form + contains + + !> Serialization interface + procedure :: serializable_is_same => fortran_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + + end type fortran_config_t + character(len=*), parameter, private :: class_name = 'fortran_config_t' + contains !> Construct a new build configuration from a TOML data structure @@ -102,4 +112,68 @@ subroutine check(table, error) end subroutine check + logical function fortran_is_same(this,that) + class(fortran_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + fortran_is_same = .false. + + select type (other=>that) + type is (fortran_config_t) + if (this%implicit_typing.neqv.other%implicit_typing) return + if (this%implicit_external.neqv.other%implicit_external) return + if (.not.allocated(this%source_form).eqv.allocated(other%source_form)) return + if (.not.this%source_form==other%source_form) return + class default + ! Not the same type + return + end select + + !> All checks passed! + fortran_is_same = .true. + + end function fortran_is_same + + !> Dump install config to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(fortran_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call set_value(table, "implicit-typing", self%implicit_typing, error, class_name) + if (allocated(error)) return + call set_value(table, "implicit-external", self%implicit_external, error, class_name) + if (allocated(error)) return + call set_string(table, "source-form", self%source_form, error, class_name) + if (allocated(error)) return + + end subroutine dump_to_toml + + !> Read install config from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(fortran_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call get_value(table, "implicit-typing", self%implicit_typing, error, class_name) + if (allocated(error)) return + call get_value(table, "implicit-external", self%implicit_external, error, class_name) + if (allocated(error)) return + call get_value(table, "source-form", self%source_form) + + end subroutine load_from_toml + + end module fpm_manifest_fortran diff --git a/src/fpm/manifest/install.f90 b/src/fpm/manifest/install.f90 index 98c2ef5321..87a0c357bd 100644 --- a/src/fpm/manifest/install.f90 +++ b/src/fpm/manifest/install.f90 @@ -24,7 +24,6 @@ module fpm_manifest_install !> Print information on this instance procedure :: info - !> Serialization interface procedure :: serializable_is_same => install_conf_same procedure :: dump_to_toml diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 909e6a4deb..28139ac9c0 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -8,6 +8,7 @@ module test_toml & new_dependency_node, new_dependency_tree, resize use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_manifest_install + use fpm_manifest_fortran use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & @@ -45,6 +46,7 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-dependency-tree-invalid", dependency_tree_invalid, should_fail=.true.), & & new_unittest("serialize-dependency-tree-invalid2", dependency_tree_invalid2, should_fail=.true.), & & new_unittest("serialize-install-config", install_config_roundtrip), & + & new_unittest("serialize-fortran-config", fortran_features_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-fortran-invalid", fft_invalid, should_fail=.true.), & @@ -1166,4 +1168,25 @@ subroutine install_config_roundtrip(error) end subroutine install_config_roundtrip + subroutine fortran_features_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(fortran_config_t) :: fortran + + integer :: loop + + fortran%implicit_external = .true. + fortran%implicit_typing = .false. + fortran%source_form = 'free' + + call fortran%test_serialization('fortran_features_roundtrip',error) + if (allocated(error)) return + + deallocate(fortran%source_form) + call fortran%test_serialization('fortran_features_roundtrip 2',error) + + end subroutine fortran_features_roundtrip + end module test_toml From 569f8988d276aebf9000b8c1e969a5e4f0191e21 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 20:00:59 +0200 Subject: [PATCH 533/799] serialize `library_config_t` --- src/fpm/manifest/library.f90 | 78 ++++++++++++++++++++++++++++++++++-- test/fpm_test/test_toml.f90 | 27 ++++++++++++- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/fpm/manifest/library.f90 b/src/fpm/manifest/library.f90 index 68ccc203ef..52e33efecb 100644 --- a/src/fpm/manifest/library.f90 +++ b/src/fpm/manifest/library.f90 @@ -10,8 +10,9 @@ !>``` module fpm_manifest_library use fpm_error, only : error_t, syntax_error - use fpm_strings, only: string_t, string_cat - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list + use fpm_strings, only: string_t, string_cat, operator(==) + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list, serializable_t, set_value, & + set_list, set_string, get_value, get_list implicit none private @@ -19,7 +20,7 @@ module fpm_manifest_library !> Configuration meta data for a library - type :: library_config_t + type, extends(serializable_t) :: library_config_t !> Source path prefix character(len=:), allocatable :: source_dir @@ -35,8 +36,15 @@ module fpm_manifest_library !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => library_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type library_config_t + character(*), parameter, private :: class_name = 'library_config_t' + contains @@ -138,5 +146,69 @@ subroutine info(self, unit, verbosity) end subroutine info + logical function library_is_same(this,that) + class(library_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + library_is_same = .false. + + select type (other=>that) + type is (library_config_t) + if (.not.this%include_dir==other%include_dir) return + if (.not.allocated(this%source_dir).eqv.allocated(other%source_dir)) return + if (.not.this%source_dir==other%source_dir) return + if (.not.allocated(this%build_script).eqv.allocated(other%build_script)) return + if (.not.this%build_script==other%build_script) return + class default + ! Not the same type + return + end select + + !> All checks passed! + library_is_same = .true. + + end function library_is_same + + !> Dump install config to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(library_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call set_string(table, "source-dir", self%source_dir, error, class_name) + if (allocated(error)) return + call set_string(table, "build-script", self%build_script, error, class_name) + if (allocated(error)) return + call set_list(table, "include-dir", self%include_dir, error) + if (allocated(error)) return + + end subroutine dump_to_toml + + !> Read install config from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(library_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call get_value(table, "source-dir", self%source_dir) + if (allocated(error)) return + call get_value(table, "build-script", self%build_script) + if (allocated(error)) return + call get_list(table, "include-dir", self%include_dir, error) + + end subroutine load_from_toml + end module fpm_manifest_library diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 28139ac9c0..c95f4468ee 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -9,6 +9,7 @@ module test_toml use fpm_manifest_dependency, only: dependency_config_t, dependency_destroy use fpm_manifest_install use fpm_manifest_fortran + use fpm_manifest_library use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & @@ -47,6 +48,7 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-dependency-tree-invalid2", dependency_tree_invalid2, should_fail=.true.), & & new_unittest("serialize-install-config", install_config_roundtrip), & & new_unittest("serialize-fortran-config", fortran_features_roundtrip), & + & new_unittest("serialize-library-config", library_config_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-fortran-invalid", fft_invalid, should_fail=.true.), & @@ -1175,8 +1177,6 @@ subroutine fortran_features_roundtrip(error) type(fortran_config_t) :: fortran - integer :: loop - fortran%implicit_external = .true. fortran%implicit_typing = .false. fortran%source_form = 'free' @@ -1189,4 +1189,27 @@ subroutine fortran_features_roundtrip(error) end subroutine fortran_features_roundtrip + subroutine library_config_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(library_config_t) :: lib + + lib%source_dir = 'lib' + lib%include_dir = [string_t('a'),string_t('b')] + + call lib%test_serialization('library_config: 1',error) + if (allocated(error)) return + + lib%build_script = 'install.sh' + + call lib%test_serialization('library_config: 2',error) + if (allocated(error)) return + + deallocate(lib%include_dir) + call lib%test_serialization('library_config: 3',error) + + end subroutine library_config_roundtrip + end module test_toml From 5c6dcfc3eef8101d52c3bb4c26668fe4a1c9e27e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 16 May 2023 20:20:54 +0200 Subject: [PATCH 534/799] serialize `executable_config_t` --- src/fpm/manifest/dependency.f90 | 41 +++++++- src/fpm/manifest/executable.f90 | 170 +++++++++++++++++++++++++++++++- test/fpm_test/test_toml.f90 | 41 ++++++++ 3 files changed, 246 insertions(+), 6 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index c3013178f5..b6f0e1e810 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -35,7 +35,7 @@ module fpm_manifest_dependency private public :: dependency_config_t, new_dependency, new_dependencies, manifest_has_changed, & - & dependency_destroy + & dependency_destroy, resize !> Configuration meta data for a dependency type, extends(serializable_t) :: dependency_config_t @@ -73,6 +73,10 @@ module fpm_manifest_dependency !> Common output format for writing to the command line character(len=*), parameter :: out_fmt = '("#", *(1x, g0))' + interface resize + module procedure resize_dependency_config + end interface resize + contains !> Construct a new dependency configuration from a TOML data structure @@ -438,4 +442,39 @@ subroutine load_from_toml(self, table, error) end subroutine load_from_toml + !> Reallocate a list of dependencies + pure subroutine resize_dependency_config(var, n) + !> Instance of the array to be resized + type(dependency_config_t), allocatable, intent(inout) :: var(:) + !> Dimension of the final array size + integer, intent(in), optional :: n + + type(dependency_config_t), allocatable :: tmp(:) + integer :: this_size, new_size + integer, parameter :: initial_size = 16 + + if (allocated(var)) then + this_size = size(var, 1) + call move_alloc(var, tmp) + else + this_size = initial_size + end if + + if (present(n)) then + new_size = n + else + new_size = this_size + this_size/2 + 1 + end if + + allocate (var(new_size)) + + if (allocated(tmp)) then + this_size = min(size(tmp, 1), size(var, 1)) + var(:this_size) = tmp(:this_size) + deallocate (tmp) + end if + + end subroutine resize_dependency_config + + end module fpm_manifest_dependency diff --git a/src/fpm/manifest/executable.f90 b/src/fpm/manifest/executable.f90 index 66bb0f2cb2..47c500a824 100644 --- a/src/fpm/manifest/executable.f90 +++ b/src/fpm/manifest/executable.f90 @@ -11,10 +11,11 @@ !>[executable.dependencies] !>``` module fpm_manifest_executable - use fpm_manifest_dependency, only : dependency_config_t, new_dependencies - use fpm_error, only : error_t, syntax_error, bad_name_error - use fpm_strings, only : string_t - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list + use fpm_manifest_dependency, only : dependency_config_t, new_dependencies, resize + use fpm_error, only : error_t, syntax_error, bad_name_error, fatal_error + use fpm_strings, only : string_t, operator(==) + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list, serializable_t, add_table, & + set_string, set_list implicit none private @@ -22,7 +23,7 @@ module fpm_manifest_executable !> Configuation meta data for an executable - type :: executable_config_t + type, extends(serializable_t) :: executable_config_t !> Name of the resulting executable character(len=:), allocatable :: name @@ -44,8 +45,15 @@ module fpm_manifest_executable !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => exe_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type executable_config_t + character(*), parameter, private :: class_name = 'executable_config_t' + contains @@ -186,4 +194,156 @@ subroutine info(self, unit, verbosity) end subroutine info + logical function exe_is_same(this,that) + class(executable_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + integer :: ii + + exe_is_same = .false. + + select type (other=>that) + type is (executable_config_t) + if (.not.this%link==other%link) return + if (.not.allocated(this%name).eqv.allocated(other%name)) return + if (.not.this%name==other%name) return + if (.not.allocated(this%source_dir).eqv.allocated(other%source_dir)) return + if (.not.this%source_dir==other%source_dir) return + if (.not.allocated(this%main).eqv.allocated(other%main)) return + if (.not.this%main==other%main) return + if (.not.allocated(this%dependency).eqv.allocated(other%dependency)) return + if (allocated(this%dependency)) then + if (.not.(size(this%dependency)==size(other%dependency))) return + do ii = 1, size(this%dependency) + if (.not.(this%dependency(ii)==other%dependency(ii))) return + end do + end if + class default + ! Not the same type + return + end select + + !> All checks passed! + exe_is_same = .true. + + end function exe_is_same + + !> Dump install config to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(executable_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + integer :: ierr, ii + type(toml_table), pointer :: ptr_deps,ptr + character(27) :: unnamed + + call set_string(table, "name", self%name, error) + if (allocated(error)) return + call set_string(table, "source-dir", self%source_dir, error) + if (allocated(error)) return + call set_string(table, "main", self%main, error) + if (allocated(error)) return + + if (allocated(self%dependency)) then + + ! Create dependency table + call add_table(table, "dependencies", ptr_deps) + if (.not. associated(ptr_deps)) then + call fatal_error(error, class_name//" cannot create dependency table ") + return + end if + + do ii = 1, size(self%dependency) + associate (dep => self%dependency(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(dep%name)==0) then + write(unnamed,1) ii + call add_table(ptr_deps, trim(unnamed), ptr) + else + call add_table(ptr_deps, dep%name, ptr) + end if + if (.not. associated(ptr)) then + call fatal_error(error, class_name//" cannot create entry for dependency "//dep%name) + return + end if + call dep%dump_to_toml(ptr, error) + if (allocated(error)) return + end associate + end do + + endif + + call set_list(table, "link", self%link, error) + if (allocated(error)) return + + 1 format('UNNAMED_DEPENDENCY_',i0) + + end subroutine dump_to_toml + + !> Read install config from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(executable_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + type(toml_key), allocatable :: keys(:),dep_keys(:) + type(toml_table), pointer :: ptr_deps,ptr + integer :: ii, jj, ierr + + call table%get_keys(keys) + + call get_value(table, "name", self%name) + if (allocated(error)) return + call get_value(table, "source-dir", self%source_dir) + if (allocated(error)) return + call get_value(table, "main", self%main) + if (allocated(error)) return + call get_list(table, "link", self%link, error) + + find_deps_table: do ii = 1, size(keys) + if (keys(ii)%key=="dependencies") then + + call get_value(table, keys(ii), ptr_deps) + if (.not.associated(ptr_deps)) then + call fatal_error(error,class_name//': error retrieving dependency table from TOML table') + return + end if + + !> Read all dependencies + call ptr_deps%get_keys(dep_keys) + call resize(self%dependency, size(dep_keys)) + + do jj = 1, size(dep_keys) + + call get_value(ptr_deps, dep_keys(jj), ptr) + call self%dependency(jj)%load_from_toml(ptr, error) + if (allocated(error)) return + + end do + + exit find_deps_table + + endif + end do find_deps_table + + end subroutine load_from_toml + + end module fpm_manifest_executable diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index c95f4468ee..a76b5c9f47 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -10,6 +10,7 @@ module test_toml use fpm_manifest_install use fpm_manifest_fortran use fpm_manifest_library + use fpm_manifest_executable use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & @@ -1212,4 +1213,44 @@ subroutine library_config_roundtrip(error) end subroutine library_config_roundtrip + + subroutine executable_config_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(executable_config_t) :: exe + type(dependency_config_t) :: dep + + exe%name = "my_executable" + exe%source_dir = 'app' + + call exe%test_serialization('executable_config: 1',error) + if (allocated(error)) return + + exe%main = 'main_program.F90' + + call exe%test_serialization('executable_config: 2',error) + if (allocated(error)) return + + exe%link = [string_t('netcdf'),string_t('hdf5')] + call exe%test_serialization('executable_config: 3',error) + + call dependency_destroy(dep) + + dep%name = "M_CLI2" + dep%path = "~/./some/dummy/path" + dep%namespace = "urbanjost" + allocate(dep%requested_version) + call new_version(dep%requested_version, "3.2.0",error); if (allocated(error)) return + + allocate(dep%git) + dep%git = git_target_revision(url="https://github.com/urbanjost/M_CLI2.git", & + sha1="7264878cdb1baff7323cc48596d829ccfe7751b8") + + allocate(exe%dependency(1),source=dep) + call exe%test_serialization('executable_config: 4',error) + + end subroutine executable_config_roundtrip + end module test_toml From 819e0ebda3504ca2190a810716f730f1278c3b3d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 17 May 2023 09:29:30 -0500 Subject: [PATCH 535/799] `preprocess_config_t`: make serializable and test --- src/fpm/manifest/fortran.f90 | 1 - src/fpm/manifest/install.f90 | 2 +- src/fpm/manifest/preprocess.f90 | 87 +++++++++++++++++++++++++++++++-- test/fpm_test/test_toml.f90 | 18 +++++++ 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/fpm/manifest/fortran.f90 b/src/fpm/manifest/fortran.f90 index 231191c433..083d61fe1e 100644 --- a/src/fpm/manifest/fortran.f90 +++ b/src/fpm/manifest/fortran.f90 @@ -25,7 +25,6 @@ module fpm_manifest_fortran procedure :: dump_to_toml procedure :: load_from_toml - end type fortran_config_t character(len=*), parameter, private :: class_name = 'fortran_config_t' diff --git a/src/fpm/manifest/install.f90 b/src/fpm/manifest/install.f90 index 87a0c357bd..5c0f46837f 100644 --- a/src/fpm/manifest/install.f90 +++ b/src/fpm/manifest/install.f90 @@ -31,7 +31,7 @@ module fpm_manifest_install end type install_config_t - character(*), parameter :: class_name = 'install_config_t' + character(*), parameter, private :: class_name = 'install_config_t' contains diff --git a/src/fpm/manifest/preprocess.f90 b/src/fpm/manifest/preprocess.f90 index 538652c29a..6d7df28871 100644 --- a/src/fpm/manifest/preprocess.f90 +++ b/src/fpm/manifest/preprocess.f90 @@ -12,15 +12,16 @@ module fpm_manifest_preprocess use fpm_error, only : error_t, syntax_error - use fpm_strings, only : string_t - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list + use fpm_strings, only : string_t, operator(==) + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list, serializable_t, set_value, set_list, & + set_string implicit none private public :: preprocess_config_t, new_preprocess_config, new_preprocessors !> Configuration meta data for a preprocessor - type :: preprocess_config_t + type, extends(serializable_t) :: preprocess_config_t !> Name of the preprocessor character(len=:), allocatable :: name @@ -39,8 +40,15 @@ module fpm_manifest_preprocess !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => preprocess_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type preprocess_config_t + character(*), parameter, private :: class_name = 'preprocess_config_t' + contains !> Construct a new preprocess configuration from TOML data structure @@ -154,7 +162,7 @@ subroutine info(self, unit, verbosity) pr = 1 end if - if (pr < 1) return + if (pr < 1) return write(unit, fmt) "Preprocessor" if (allocated(self%name)) then @@ -181,4 +189,75 @@ subroutine info(self, unit, verbosity) end subroutine info + logical function preprocess_is_same(this,that) + class(preprocess_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + preprocess_is_same = .false. + + select type (other=>that) + type is (preprocess_config_t) + if (allocated(this%name).neqv.allocated(other%name)) return + if (allocated(this%name)) then + if (.not.(this%name==other%name)) return + endif + if (.not.(this%suffixes==other%suffixes)) return + if (.not.(this%directories==other%directories)) return + if (.not.(this%macros==other%macros)) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + preprocess_is_same = .true. + + end function preprocess_is_same + + !> Dump install config to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(preprocess_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call set_string(table, "name", self%name, error) + if (allocated(error)) return + call set_list(table, "suffixes", self%suffixes, error) + if (allocated(error)) return + call set_list(table, "directories", self%directories, error) + if (allocated(error)) return + call set_list(table, "macros", self%macros, error) + if (allocated(error)) return + + end subroutine dump_to_toml + + !> Read install config from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(preprocess_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call get_value(table, "name", self%name) + call get_list(table, "suffixes", self%suffixes, error) + if (allocated(error)) return + call get_list(table, "directories", self%directories, error) + if (allocated(error)) return + call get_list(table, "macros", self%macros, error) + if (allocated(error)) return + + end subroutine load_from_toml + end module fpm_manifest_preprocess diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index a76b5c9f47..2a0301d2a5 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -11,6 +11,7 @@ module test_toml use fpm_manifest_fortran use fpm_manifest_library use fpm_manifest_executable + use fpm_manifest_preprocess use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & @@ -50,6 +51,8 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-install-config", install_config_roundtrip), & & new_unittest("serialize-fortran-config", fortran_features_roundtrip), & & new_unittest("serialize-library-config", library_config_roundtrip), & + & new_unittest("serialize-executable-config", executable_config_roundtrip), & + & new_unittest("serialize-preprocess-config", preprocess_config_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-fortran-invalid", fft_invalid, should_fail=.true.), & @@ -1253,4 +1256,19 @@ subroutine executable_config_roundtrip(error) end subroutine executable_config_roundtrip + + subroutine preprocess_config_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(preprocess_config_t) :: prep + + prep%name = "preprocessor config" + prep%macros = [string_t('Whatever'),string_t('FPM_BOOTSTRAP')] + + call prep%test_serialization('preprocess_config', error) + + end subroutine preprocess_config_roundtrip + end module test_toml From 24d9da09b1b2110b95b87d097d0bb8a03823ec5f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 14:24:05 +0200 Subject: [PATCH 536/799] serialize `file_scope_flag` and test --- src/fpm/manifest/profiles.f90 | 120 +++++++++++++++++++++++++++------- test/fpm_test/test_toml.f90 | 19 ++++++ 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 2e84f0c6e9..44fe65ad3a 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -43,7 +43,7 @@ !> module fpm_manifest_profile use fpm_error, only : error_t, syntax_error, fatal_error, fpm_stop - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, set_string use fpm_strings, only: lower use fpm_environment, only: get_os_type, OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD @@ -53,12 +53,12 @@ module fpm_manifest_profile & info_profile, find_profile, DEFAULT_COMPILER !> Name of the default compiler - character(len=*), parameter :: DEFAULT_COMPILER = 'gfortran' + character(len=*), parameter :: DEFAULT_COMPILER = 'gfortran' integer, parameter :: OS_ALL = -1 character(len=:), allocatable :: path !> Type storing file name - file scope compiler flags pairs - type :: file_scope_flag + type, extends(serializable_t) :: file_scope_flag !> Name of the file character(len=:), allocatable :: file_name @@ -66,6 +66,13 @@ module fpm_manifest_profile !> File scope flags character(len=:), allocatable :: flags + contains + + !> Serialization interface + procedure :: serializable_is_same => file_scope_same + procedure :: dump_to_toml => file_scope_dump + procedure :: load_from_toml => file_scope_load + end type file_scope_flag !> Configuration meta data for a profile @@ -78,7 +85,7 @@ module fpm_manifest_profile !> Value repesenting OS integer :: os_type - + !> Fortran compiler flags character(len=:), allocatable :: flags @@ -110,16 +117,16 @@ module fpm_manifest_profile function new_profile(profile_name, compiler, os_type, flags, c_flags, cxx_flags, & link_time_flags, file_scope_flags, is_built_in) & & result(profile) - + !> Name of the profile character(len=*), intent(in) :: profile_name - + !> Name of the compiler character(len=*), intent(in) :: compiler - + !> Type of the OS integer, intent(in) :: os_type - + !> Fortran compiler flags character(len=*), optional, intent(in) :: flags @@ -190,7 +197,7 @@ subroutine validate_compiler_name(compiler_name, is_valid) is_valid = .false. end select end subroutine validate_compiler_name - + !> Check if os_name is a valid name of a supported OS subroutine validate_os_name(os_name, is_valid) @@ -373,10 +380,10 @@ subroutine get_flags(profile_name, compiler_name, os_type, key_list, table, prof & flags, c_flags, cxx_flags, link_time_flags, file_scope_flags) profindex = profindex + 1 end subroutine get_flags - + !> Traverse operating system tables to obtain number of profiles subroutine traverse_oss_for_size(profile_name, compiler_name, os_list, table, profiles_size, error) - + !> Name of profile character(len=:), allocatable, intent(in) :: profile_name @@ -447,7 +454,7 @@ end subroutine traverse_oss_for_size !> Traverse operating system tables to obtain profiles subroutine traverse_oss(profile_name, compiler_name, os_list, table, profiles, profindex, error) - + !> Name of profile character(len=:), allocatable, intent(in) :: profile_name @@ -468,7 +475,7 @@ subroutine traverse_oss(profile_name, compiler_name, os_list, table, profiles, p !> Index in the list of profiles integer, intent(inout) :: profindex - + type(toml_key), allocatable :: key_list(:) character(len=:), allocatable :: os_name, l_os_name type(toml_table), pointer :: os_node @@ -513,7 +520,7 @@ end subroutine traverse_oss !> Traverse compiler tables subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_size, profiles, profindex) - + !> Name of profile character(len=:), allocatable, intent(in) :: profile_name @@ -522,10 +529,10 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si !> Table containing compiler tables type(toml_table), pointer, intent(in) :: table - + !> Error handling type(error_t), allocatable, intent(out) :: error - + !> Number of profiles in list of profiles integer, intent(inout), optional :: profiles_size @@ -534,8 +541,8 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si !> Index in the list of profiles integer, intent(inout), optional :: profindex - - character(len=:), allocatable :: compiler_name + + character(len=:), allocatable :: compiler_name type(toml_table), pointer :: comp_node type(toml_key), allocatable :: os_list(:) integer :: icomp, stat @@ -544,7 +551,7 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si if (size(comp_list)<1) return do icomp = 1, size(comp_list) call validate_compiler_name(comp_list(icomp)%key, is_valid) - if (is_valid) then + if (is_valid) then compiler_name = comp_list(icomp)%key call get_value(table, compiler_name, comp_node, stat=stat) if (stat /= toml_stat%success) then @@ -567,7 +574,7 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si else call fatal_error(error,'*traverse_compilers*:Error: Compiler name not specified or invalid.') end if - end do + end do end subroutine traverse_compilers !> Construct new profiles array from a TOML data structure @@ -596,9 +603,9 @@ subroutine new_profiles(profiles, table, error) default_profiles = get_default_profiles(error) if (allocated(error)) return call table%get_keys(prof_list) - + if (size(prof_list) < 1) return - + profiles_size = 0 do iprof = 1, size(prof_list) @@ -633,7 +640,7 @@ subroutine new_profiles(profiles, table, error) profiles_size = profiles_size + size(default_profiles) allocate(profiles(profiles_size)) - + do profindex=1, size(default_profiles) profiles(profindex) = default_profiles(profindex) end do @@ -954,4 +961,71 @@ subroutine find_profile(profiles, profile_name, compiler, os_type, found_matchin end do end if end subroutine find_profile + + + logical function file_scope_same(this,that) + class(file_scope_flag), intent(in) :: this + class(serializable_t), intent(in) :: that + + file_scope_same = .false. + + select type (other=>that) + type is (file_scope_flag) + if (allocated(this%file_name).neqv.allocated(other%file_name)) return + if (allocated(this%file_name)) then + if (.not.(this%file_name==other%file_name)) return + endif + if (allocated(this%flags).neqv.allocated(other%flags)) return + if (allocated(this%flags)) then + if (.not.(this%flags==other%flags)) return + endif + + class default + ! Not the same type + return + end select + + !> All checks passed! + file_scope_same = .true. + + end function file_scope_same + + !> Dump to toml table + subroutine file_scope_dump(self, table, error) + + !> Instance of the serializable object + class(file_scope_flag), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call set_string(table, "file-name", self%file_name, error) + if (allocated(error)) return + call set_string(table, "flags", self%flags, error) + if (allocated(error)) return + + end subroutine file_scope_dump + + !> Read from toml table (no checks made at this stage) + subroutine file_scope_load(self, table, error) + + !> Instance of the serializable object + class(file_scope_flag), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + call get_value(table, "file-name", self%file_name) + call get_value(table, "flags", self%flags) + + end subroutine file_scope_load + + + end module fpm_manifest_profile diff --git a/test/fpm_test/test_toml.f90 b/test/fpm_test/test_toml.f90 index 2a0301d2a5..ad0a8b40c9 100644 --- a/test/fpm_test/test_toml.f90 +++ b/test/fpm_test/test_toml.f90 @@ -12,6 +12,7 @@ module test_toml use fpm_manifest_library use fpm_manifest_executable use fpm_manifest_preprocess + use fpm_manifest_profile use fpm_versioning, only: new_version use fpm_strings, only: string_t, operator(==), split use fpm_model, only: fortran_features_t, package_t, FPM_SCOPE_LIB, FPM_UNIT_MODULE, fpm_model_t, & @@ -53,6 +54,7 @@ subroutine collect_toml(testsuite) & new_unittest("serialize-library-config", library_config_roundtrip), & & new_unittest("serialize-executable-config", executable_config_roundtrip), & & new_unittest("serialize-preprocess-config", preprocess_config_roundtrip), & + & new_unittest("serialize-file-scope-flag", file_scope_flag_roundtrip), & & new_unittest("serialize-string-array", string_array_roundtrip), & & new_unittest("serialize-fortran-features", fft_roundtrip), & & new_unittest("serialize-fortran-invalid", fft_invalid, should_fail=.true.), & @@ -1271,4 +1273,21 @@ subroutine preprocess_config_roundtrip(error) end subroutine preprocess_config_roundtrip + subroutine file_scope_flag_roundtrip(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(file_scope_flag) :: ff + + call ff%test_serialization('file_scope_flag: empty', error) + if (allocated(error)) return + + ff%file_name = "preprocessor config" + ff%flags = "-1 -f -2 -g" + + call ff%test_serialization('file_scope_flag: non-empty', error) + + end subroutine file_scope_flag_roundtrip + end module test_toml From dd81493210c4fe48882e44653550d4ef2af25fad Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 14:51:23 +0200 Subject: [PATCH 537/799] partial `profile_config_t` --- src/fpm/manifest/profiles.f90 | 153 +++++++++++++++++++++++++++++++++- src/fpm_command_line.f90 | 14 +--- src/fpm_environment.f90 | 20 +++++ 3 files changed, 172 insertions(+), 15 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 44fe65ad3a..15a0be74be 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -43,10 +43,11 @@ !> module fpm_manifest_profile use fpm_error, only : error_t, syntax_error, fatal_error, fpm_stop - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, set_string + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, & + set_string, add_table use fpm_strings, only: lower use fpm_environment, only: get_os_type, OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, OS_NAME use fpm_filesystem, only: join_path implicit none public :: profile_config_t, new_profile, new_profiles, get_default_profiles, & @@ -76,7 +77,7 @@ module fpm_manifest_profile end type file_scope_flag !> Configuration meta data for a profile - type :: profile_config_t + type, extends(serializable_t) :: profile_config_t !> Name of the profile character(len=:), allocatable :: profile_name @@ -109,6 +110,11 @@ module fpm_manifest_profile !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => profile_same + procedure :: dump_to_toml => profile_dump + procedure :: load_from_toml => profile_load + end type profile_config_t contains @@ -1026,6 +1032,147 @@ subroutine file_scope_load(self, table, error) end subroutine file_scope_load + logical function profile_same(this,that) + class(profile_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + integer :: ii + + profile_same = .false. + + select type (other=>that) + type is (profile_config_t) + if (allocated(this%profile_name).neqv.allocated(other%profile_name)) return + if (allocated(this%profile_name)) then + if (.not.(this%profile_name==other%profile_name)) return + endif + if (allocated(this%compiler).neqv.allocated(other%compiler)) return + if (allocated(this%compiler)) then + if (.not.(this%compiler==other%compiler)) return + endif + if (this%os_type/=other%os_type) return + if (allocated(this%flags).neqv.allocated(other%flags)) return + if (allocated(this%flags)) then + if (.not.(this%flags==other%flags)) return + endif + if (allocated(this%c_flags).neqv.allocated(other%c_flags)) return + if (allocated(this%c_flags)) then + if (.not.(this%c_flags==other%c_flags)) return + endif + if (allocated(this%cxx_flags).neqv.allocated(other%cxx_flags)) return + if (allocated(this%cxx_flags)) then + if (.not.(this%cxx_flags==other%cxx_flags)) return + endif + if (allocated(this%link_time_flags).neqv.allocated(other%link_time_flags)) return + if (allocated(this%link_time_flags)) then + if (.not.(this%link_time_flags==other%link_time_flags)) return + endif + + if (allocated(this%file_scope_flags).neqv.allocated(other%file_scope_flags)) return + if (allocated(this%file_scope_flags)) then + if (.not.size(this%file_scope_flags)==size(other%file_scope_flags)) return + do ii=1,size(this%file_scope_flags) + if (.not.this%file_scope_flags(ii)==other%file_scope_flags(ii)) return + end do + endif + + if (this%is_built_in.neqv.other%is_built_in) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + profile_same = .true. + + end function profile_same + + !> Dump to toml table + subroutine profile_dump(self, table, error) + + !> Instance of the serializable object + class(profile_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + integer :: ierr, ii + type(toml_table), pointer :: ptr_deps, ptr + character(len=30) :: unnamed + + call set_string(table, "profile-name", self%profile_name, error) + if (allocated(error)) return + call set_string(table, "compiler", self%compiler, error) + if (allocated(error)) return + call set_string(table,"os-type",OS_NAME(self%os_type), error, 'profile_config_t') + if (allocated(error)) return + call set_string(table, "flags", self%flags, error) + if (allocated(error)) return + call set_string(table, "c-flags", self%c_flags, error) + if (allocated(error)) return + call set_string(table, "cxx-flags", self%cxx_flags, error) + if (allocated(error)) return + call set_string(table, "link-time-flags", self%link_time_flags, error) + if (allocated(error)) return + + if (allocated(self%file_scope_flags)) then + + ! Create dependency table + call add_table(table, "file-scope-flags", ptr_deps) + if (.not. associated(ptr_deps)) then + call fatal_error(error, "profile_config_t cannot create file scope table ") + return + end if + + do ii = 1, size(self%file_scope_flags) + associate (dep => self%file_scope_flags(ii)) + + !> Because files need a name, fallback if this has no name + if (len_trim(dep%file_name)==0) then + write(unnamed,1) ii + call add_table(ptr_deps, trim(unnamed), ptr) + else + call add_table(ptr_deps, dep%file_name, ptr) + end if + if (.not. associated(ptr)) then + call fatal_error(error, "profile_config_t cannot create entry for file "//dep%file_name) + return + end if + call dep%dump_to_toml(ptr, error) + if (allocated(error)) return + end associate + end do + + endif + + call set_value(table, "is-built-in", self%is_built_in, error, 'profile_config_t') + if (allocated(error)) return + + 1 format('UNNAMED_FILE_',i0) + + end subroutine profile_dump + + !> Read from toml table (no checks made at this stage) + subroutine profile_load(self, table, error) + + !> Instance of the serializable object + class(profile_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + +! call get_value(table, "file-name", self%profile_name) +! call get_value(table, "flags", self%flags) + + end subroutine profile_load end module fpm_manifest_profile diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 3b723c74c7..8cd4776b75 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -25,7 +25,7 @@ module fpm_command_line use fpm_environment, only : get_os_type, get_env, os_is_unix, & OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, OS_NAME use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified use M_CLI2, only : get_subcommand, CLI_RESPONSE_FILE use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name @@ -234,17 +234,7 @@ subroutine get_command_line_settings(cmd_settings) call set_help() os = get_os_type() ! text for --version switch, - select case (os) - case (OS_LINUX); os_type = "OS Type: Linux" - case (OS_MACOS); os_type = "OS Type: macOS" - case (OS_WINDOWS); os_type = "OS Type: Windows" - case (OS_CYGWIN); os_type = "OS Type: Cygwin" - case (OS_SOLARIS); os_type = "OS Type: Solaris" - case (OS_FREEBSD); os_type = "OS Type: FreeBSD" - case (OS_OPENBSD); os_type = "OS Type: OpenBSD" - case (OS_UNKNOWN); os_type = "OS Type: Unknown" - case default ; os_type = "OS Type: UNKNOWN" - end select + os_type = "OS Type: "//OS_NAME(os) is_unix = os_is_unix(os) ! Get current release version diff --git a/src/fpm_environment.f90 b/src/fpm_environment.f90 index 7e8aa2317d..39152ab4ad 100644 --- a/src/fpm_environment.f90 +++ b/src/fpm_environment.f90 @@ -15,6 +15,7 @@ module fpm_environment public :: get_command_arguments_quoted public :: separator + public :: OS_NAME integer, parameter, public :: OS_UNKNOWN = 0 integer, parameter, public :: OS_LINUX = 1 integer, parameter, public :: OS_MACOS = 2 @@ -24,6 +25,25 @@ module fpm_environment integer, parameter, public :: OS_FREEBSD = 6 integer, parameter, public :: OS_OPENBSD = 7 contains + + !> Return string describing the OS type flag + pure function OS_NAME(os) + integer, intent(in) :: os + character(len=:), allocatable :: OS_NAME + + select case (os) + case (OS_LINUX); OS_NAME = "Linux" + case (OS_MACOS); OS_NAME = "macOS" + case (OS_WINDOWS); OS_NAME = "Windows" + case (OS_CYGWIN); OS_NAME = "Cygwin" + case (OS_SOLARIS); OS_NAME = "Solaris" + case (OS_FREEBSD); OS_NAME = "FreeBSD" + case (OS_OPENBSD); OS_NAME = "OpenBSD" + case (OS_UNKNOWN); OS_NAME = "Unknown" + case default ; OS_NAME = "UNKNOWN" + end select + end function OS_NAME + !> Determine the OS type integer function get_os_type() result(r) !! From fa8c98efd596f6b57a906765e68f58baab322cfa Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 14:51:30 +0200 Subject: [PATCH 538/799] Revert "partial `profile_config_t`" This reverts commit dd81493210c4fe48882e44653550d4ef2af25fad. --- src/fpm/manifest/profiles.f90 | 153 +--------------------------------- src/fpm_command_line.f90 | 14 +++- src/fpm_environment.f90 | 20 ----- 3 files changed, 15 insertions(+), 172 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 15a0be74be..44fe65ad3a 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -43,11 +43,10 @@ !> module fpm_manifest_profile use fpm_error, only : error_t, syntax_error, fatal_error, fpm_stop - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, & - set_string, add_table + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, set_string use fpm_strings, only: lower use fpm_environment, only: get_os_type, OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, OS_NAME + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use fpm_filesystem, only: join_path implicit none public :: profile_config_t, new_profile, new_profiles, get_default_profiles, & @@ -77,7 +76,7 @@ module fpm_manifest_profile end type file_scope_flag !> Configuration meta data for a profile - type, extends(serializable_t) :: profile_config_t + type :: profile_config_t !> Name of the profile character(len=:), allocatable :: profile_name @@ -110,11 +109,6 @@ module fpm_manifest_profile !> Print information on this instance procedure :: info - !> Serialization interface - procedure :: serializable_is_same => profile_same - procedure :: dump_to_toml => profile_dump - procedure :: load_from_toml => profile_load - end type profile_config_t contains @@ -1032,147 +1026,6 @@ subroutine file_scope_load(self, table, error) end subroutine file_scope_load - logical function profile_same(this,that) - class(profile_config_t), intent(in) :: this - class(serializable_t), intent(in) :: that - - integer :: ii - - profile_same = .false. - - select type (other=>that) - type is (profile_config_t) - if (allocated(this%profile_name).neqv.allocated(other%profile_name)) return - if (allocated(this%profile_name)) then - if (.not.(this%profile_name==other%profile_name)) return - endif - if (allocated(this%compiler).neqv.allocated(other%compiler)) return - if (allocated(this%compiler)) then - if (.not.(this%compiler==other%compiler)) return - endif - if (this%os_type/=other%os_type) return - if (allocated(this%flags).neqv.allocated(other%flags)) return - if (allocated(this%flags)) then - if (.not.(this%flags==other%flags)) return - endif - if (allocated(this%c_flags).neqv.allocated(other%c_flags)) return - if (allocated(this%c_flags)) then - if (.not.(this%c_flags==other%c_flags)) return - endif - if (allocated(this%cxx_flags).neqv.allocated(other%cxx_flags)) return - if (allocated(this%cxx_flags)) then - if (.not.(this%cxx_flags==other%cxx_flags)) return - endif - if (allocated(this%link_time_flags).neqv.allocated(other%link_time_flags)) return - if (allocated(this%link_time_flags)) then - if (.not.(this%link_time_flags==other%link_time_flags)) return - endif - - if (allocated(this%file_scope_flags).neqv.allocated(other%file_scope_flags)) return - if (allocated(this%file_scope_flags)) then - if (.not.size(this%file_scope_flags)==size(other%file_scope_flags)) return - do ii=1,size(this%file_scope_flags) - if (.not.this%file_scope_flags(ii)==other%file_scope_flags(ii)) return - end do - endif - - if (this%is_built_in.neqv.other%is_built_in) return - - class default - ! Not the same type - return - end select - - !> All checks passed! - profile_same = .true. - - end function profile_same - - !> Dump to toml table - subroutine profile_dump(self, table, error) - - !> Instance of the serializable object - class(profile_config_t), intent(inout) :: self - - !> Data structure - type(toml_table), intent(inout) :: table - - !> Error handling - type(error_t), allocatable, intent(out) :: error - - !> Local variables - integer :: ierr, ii - type(toml_table), pointer :: ptr_deps, ptr - character(len=30) :: unnamed - - call set_string(table, "profile-name", self%profile_name, error) - if (allocated(error)) return - call set_string(table, "compiler", self%compiler, error) - if (allocated(error)) return - call set_string(table,"os-type",OS_NAME(self%os_type), error, 'profile_config_t') - if (allocated(error)) return - call set_string(table, "flags", self%flags, error) - if (allocated(error)) return - call set_string(table, "c-flags", self%c_flags, error) - if (allocated(error)) return - call set_string(table, "cxx-flags", self%cxx_flags, error) - if (allocated(error)) return - call set_string(table, "link-time-flags", self%link_time_flags, error) - if (allocated(error)) return - - if (allocated(self%file_scope_flags)) then - - ! Create dependency table - call add_table(table, "file-scope-flags", ptr_deps) - if (.not. associated(ptr_deps)) then - call fatal_error(error, "profile_config_t cannot create file scope table ") - return - end if - - do ii = 1, size(self%file_scope_flags) - associate (dep => self%file_scope_flags(ii)) - - !> Because files need a name, fallback if this has no name - if (len_trim(dep%file_name)==0) then - write(unnamed,1) ii - call add_table(ptr_deps, trim(unnamed), ptr) - else - call add_table(ptr_deps, dep%file_name, ptr) - end if - if (.not. associated(ptr)) then - call fatal_error(error, "profile_config_t cannot create entry for file "//dep%file_name) - return - end if - call dep%dump_to_toml(ptr, error) - if (allocated(error)) return - end associate - end do - - endif - - call set_value(table, "is-built-in", self%is_built_in, error, 'profile_config_t') - if (allocated(error)) return - - 1 format('UNNAMED_FILE_',i0) - - end subroutine profile_dump - - !> Read from toml table (no checks made at this stage) - subroutine profile_load(self, table, error) - - !> Instance of the serializable object - class(profile_config_t), intent(inout) :: self - - !> Data structure - type(toml_table), intent(inout) :: table - - !> Error handling - type(error_t), allocatable, intent(out) :: error - -! call get_value(table, "file-name", self%profile_name) -! call get_value(table, "flags", self%flags) - - end subroutine profile_load end module fpm_manifest_profile diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 8cd4776b75..3b723c74c7 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -25,7 +25,7 @@ module fpm_command_line use fpm_environment, only : get_os_type, get_env, os_is_unix, & OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, OS_NAME + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified use M_CLI2, only : get_subcommand, CLI_RESPONSE_FILE use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name @@ -234,7 +234,17 @@ subroutine get_command_line_settings(cmd_settings) call set_help() os = get_os_type() ! text for --version switch, - os_type = "OS Type: "//OS_NAME(os) + select case (os) + case (OS_LINUX); os_type = "OS Type: Linux" + case (OS_MACOS); os_type = "OS Type: macOS" + case (OS_WINDOWS); os_type = "OS Type: Windows" + case (OS_CYGWIN); os_type = "OS Type: Cygwin" + case (OS_SOLARIS); os_type = "OS Type: Solaris" + case (OS_FREEBSD); os_type = "OS Type: FreeBSD" + case (OS_OPENBSD); os_type = "OS Type: OpenBSD" + case (OS_UNKNOWN); os_type = "OS Type: Unknown" + case default ; os_type = "OS Type: UNKNOWN" + end select is_unix = os_is_unix(os) ! Get current release version diff --git a/src/fpm_environment.f90 b/src/fpm_environment.f90 index 39152ab4ad..7e8aa2317d 100644 --- a/src/fpm_environment.f90 +++ b/src/fpm_environment.f90 @@ -15,7 +15,6 @@ module fpm_environment public :: get_command_arguments_quoted public :: separator - public :: OS_NAME integer, parameter, public :: OS_UNKNOWN = 0 integer, parameter, public :: OS_LINUX = 1 integer, parameter, public :: OS_MACOS = 2 @@ -25,25 +24,6 @@ module fpm_environment integer, parameter, public :: OS_FREEBSD = 6 integer, parameter, public :: OS_OPENBSD = 7 contains - - !> Return string describing the OS type flag - pure function OS_NAME(os) - integer, intent(in) :: os - character(len=:), allocatable :: OS_NAME - - select case (os) - case (OS_LINUX); OS_NAME = "Linux" - case (OS_MACOS); OS_NAME = "macOS" - case (OS_WINDOWS); OS_NAME = "Windows" - case (OS_CYGWIN); OS_NAME = "Cygwin" - case (OS_SOLARIS); OS_NAME = "Solaris" - case (OS_FREEBSD); OS_NAME = "FreeBSD" - case (OS_OPENBSD); OS_NAME = "OpenBSD" - case (OS_UNKNOWN); OS_NAME = "Unknown" - case default ; OS_NAME = "UNKNOWN" - end select - end function OS_NAME - !> Determine the OS type integer function get_os_type() result(r) !! From a7cb2a011923159b3f50a127559efe7718a86747 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 14:52:01 +0200 Subject: [PATCH 539/799] Revert "Revert "partial `profile_config_t`"" This reverts commit fa8c98efd596f6b57a906765e68f58baab322cfa. --- src/fpm/manifest/profiles.f90 | 153 +++++++++++++++++++++++++++++++++- src/fpm_command_line.f90 | 14 +--- src/fpm_environment.f90 | 20 +++++ 3 files changed, 172 insertions(+), 15 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 44fe65ad3a..15a0be74be 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -43,10 +43,11 @@ !> module fpm_manifest_profile use fpm_error, only : error_t, syntax_error, fatal_error, fpm_stop - use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, set_string + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, serializable_t, set_value, & + set_string, add_table use fpm_strings, only: lower use fpm_environment, only: get_os_type, OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, OS_NAME use fpm_filesystem, only: join_path implicit none public :: profile_config_t, new_profile, new_profiles, get_default_profiles, & @@ -76,7 +77,7 @@ module fpm_manifest_profile end type file_scope_flag !> Configuration meta data for a profile - type :: profile_config_t + type, extends(serializable_t) :: profile_config_t !> Name of the profile character(len=:), allocatable :: profile_name @@ -109,6 +110,11 @@ module fpm_manifest_profile !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => profile_same + procedure :: dump_to_toml => profile_dump + procedure :: load_from_toml => profile_load + end type profile_config_t contains @@ -1026,6 +1032,147 @@ subroutine file_scope_load(self, table, error) end subroutine file_scope_load + logical function profile_same(this,that) + class(profile_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + integer :: ii + + profile_same = .false. + + select type (other=>that) + type is (profile_config_t) + if (allocated(this%profile_name).neqv.allocated(other%profile_name)) return + if (allocated(this%profile_name)) then + if (.not.(this%profile_name==other%profile_name)) return + endif + if (allocated(this%compiler).neqv.allocated(other%compiler)) return + if (allocated(this%compiler)) then + if (.not.(this%compiler==other%compiler)) return + endif + if (this%os_type/=other%os_type) return + if (allocated(this%flags).neqv.allocated(other%flags)) return + if (allocated(this%flags)) then + if (.not.(this%flags==other%flags)) return + endif + if (allocated(this%c_flags).neqv.allocated(other%c_flags)) return + if (allocated(this%c_flags)) then + if (.not.(this%c_flags==other%c_flags)) return + endif + if (allocated(this%cxx_flags).neqv.allocated(other%cxx_flags)) return + if (allocated(this%cxx_flags)) then + if (.not.(this%cxx_flags==other%cxx_flags)) return + endif + if (allocated(this%link_time_flags).neqv.allocated(other%link_time_flags)) return + if (allocated(this%link_time_flags)) then + if (.not.(this%link_time_flags==other%link_time_flags)) return + endif + + if (allocated(this%file_scope_flags).neqv.allocated(other%file_scope_flags)) return + if (allocated(this%file_scope_flags)) then + if (.not.size(this%file_scope_flags)==size(other%file_scope_flags)) return + do ii=1,size(this%file_scope_flags) + if (.not.this%file_scope_flags(ii)==other%file_scope_flags(ii)) return + end do + endif + + if (this%is_built_in.neqv.other%is_built_in) return + + class default + ! Not the same type + return + end select + + !> All checks passed! + profile_same = .true. + + end function profile_same + + !> Dump to toml table + subroutine profile_dump(self, table, error) + + !> Instance of the serializable object + class(profile_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + !> Local variables + integer :: ierr, ii + type(toml_table), pointer :: ptr_deps, ptr + character(len=30) :: unnamed + + call set_string(table, "profile-name", self%profile_name, error) + if (allocated(error)) return + call set_string(table, "compiler", self%compiler, error) + if (allocated(error)) return + call set_string(table,"os-type",OS_NAME(self%os_type), error, 'profile_config_t') + if (allocated(error)) return + call set_string(table, "flags", self%flags, error) + if (allocated(error)) return + call set_string(table, "c-flags", self%c_flags, error) + if (allocated(error)) return + call set_string(table, "cxx-flags", self%cxx_flags, error) + if (allocated(error)) return + call set_string(table, "link-time-flags", self%link_time_flags, error) + if (allocated(error)) return + + if (allocated(self%file_scope_flags)) then + + ! Create dependency table + call add_table(table, "file-scope-flags", ptr_deps) + if (.not. associated(ptr_deps)) then + call fatal_error(error, "profile_config_t cannot create file scope table ") + return + end if + + do ii = 1, size(self%file_scope_flags) + associate (dep => self%file_scope_flags(ii)) + + !> Because files need a name, fallback if this has no name + if (len_trim(dep%file_name)==0) then + write(unnamed,1) ii + call add_table(ptr_deps, trim(unnamed), ptr) + else + call add_table(ptr_deps, dep%file_name, ptr) + end if + if (.not. associated(ptr)) then + call fatal_error(error, "profile_config_t cannot create entry for file "//dep%file_name) + return + end if + call dep%dump_to_toml(ptr, error) + if (allocated(error)) return + end associate + end do + + endif + + call set_value(table, "is-built-in", self%is_built_in, error, 'profile_config_t') + if (allocated(error)) return + + 1 format('UNNAMED_FILE_',i0) + + end subroutine profile_dump + + !> Read from toml table (no checks made at this stage) + subroutine profile_load(self, table, error) + + !> Instance of the serializable object + class(profile_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + +! call get_value(table, "file-name", self%profile_name) +! call get_value(table, "flags", self%flags) + + end subroutine profile_load end module fpm_manifest_profile diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 3b723c74c7..8cd4776b75 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -25,7 +25,7 @@ module fpm_command_line use fpm_environment, only : get_os_type, get_env, os_is_unix, & OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & - OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD + OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD, OS_NAME use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified use M_CLI2, only : get_subcommand, CLI_RESPONSE_FILE use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name @@ -234,17 +234,7 @@ subroutine get_command_line_settings(cmd_settings) call set_help() os = get_os_type() ! text for --version switch, - select case (os) - case (OS_LINUX); os_type = "OS Type: Linux" - case (OS_MACOS); os_type = "OS Type: macOS" - case (OS_WINDOWS); os_type = "OS Type: Windows" - case (OS_CYGWIN); os_type = "OS Type: Cygwin" - case (OS_SOLARIS); os_type = "OS Type: Solaris" - case (OS_FREEBSD); os_type = "OS Type: FreeBSD" - case (OS_OPENBSD); os_type = "OS Type: OpenBSD" - case (OS_UNKNOWN); os_type = "OS Type: Unknown" - case default ; os_type = "OS Type: UNKNOWN" - end select + os_type = "OS Type: "//OS_NAME(os) is_unix = os_is_unix(os) ! Get current release version diff --git a/src/fpm_environment.f90 b/src/fpm_environment.f90 index 7e8aa2317d..39152ab4ad 100644 --- a/src/fpm_environment.f90 +++ b/src/fpm_environment.f90 @@ -15,6 +15,7 @@ module fpm_environment public :: get_command_arguments_quoted public :: separator + public :: OS_NAME integer, parameter, public :: OS_UNKNOWN = 0 integer, parameter, public :: OS_LINUX = 1 integer, parameter, public :: OS_MACOS = 2 @@ -24,6 +25,25 @@ module fpm_environment integer, parameter, public :: OS_FREEBSD = 6 integer, parameter, public :: OS_OPENBSD = 7 contains + + !> Return string describing the OS type flag + pure function OS_NAME(os) + integer, intent(in) :: os + character(len=:), allocatable :: OS_NAME + + select case (os) + case (OS_LINUX); OS_NAME = "Linux" + case (OS_MACOS); OS_NAME = "macOS" + case (OS_WINDOWS); OS_NAME = "Windows" + case (OS_CYGWIN); OS_NAME = "Cygwin" + case (OS_SOLARIS); OS_NAME = "Solaris" + case (OS_FREEBSD); OS_NAME = "FreeBSD" + case (OS_OPENBSD); OS_NAME = "OpenBSD" + case (OS_UNKNOWN); OS_NAME = "Unknown" + case default ; OS_NAME = "UNKNOWN" + end select + end function OS_NAME + !> Determine the OS type integer function get_os_type() result(r) !! From 54f1231db987ea8c445f4733b4361936ccd0f925 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 15:21:35 +0200 Subject: [PATCH 540/799] serialize `profile_config_t`, test, bugfix OS check --- src/fpm/manifest/profiles.f90 | 87 ++++++++++++++++++++++++++++++--- test/fpm_test/test_manifest.f90 | 16 ++++++ 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 15a0be74be..1852118374 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -85,7 +85,7 @@ module fpm_manifest_profile character(len=:), allocatable :: compiler !> Value repesenting OS - integer :: os_type + integer :: os_type = OS_ALL !> Fortran compiler flags character(len=:), allocatable :: flags @@ -103,7 +103,7 @@ module fpm_manifest_profile type(file_scope_flag), allocatable :: file_scope_flags(:) !> Is this profile one of the built-in ones? - logical :: is_built_in + logical :: is_built_in = .false. contains @@ -234,7 +234,8 @@ subroutine match_os_type(os_name, os_type) select case (os_name) case ("linux"); os_type = OS_LINUX - case ("macos"); os_type = OS_WINDOWS + case ("macos"); os_type = OS_MACOS + case ("windows"); os_type = OS_WINDOWS case ("cygwin"); os_type = OS_CYGWIN case ("solaris"); os_type = OS_SOLARIS case ("freebsd"); os_type = OS_FREEBSD @@ -245,6 +246,22 @@ subroutine match_os_type(os_name, os_type) end subroutine match_os_type + !> Match lowercase string with name of OS to os_type enum + function os_type_name(os_type) + + !> Name of operating system + character(len=:), allocatable :: os_type_name + + !> Enum representing type of OS + integer, intent(in) :: os_type + + select case (os_type) + case (OS_ALL); os_type_name = "all" + case default; os_type_name = lower(OS_NAME(os_type)) + end select + + end function os_type_name + subroutine validate_profile_table(profile_name, compiler_name, key_list, table, error, os_valid) !> Name of profile @@ -849,7 +866,7 @@ subroutine info(self, unit, verbosity) write(unit, fmt) "- compiler", self%compiler end if - write(unit, fmt) "- os", self%os_type + write(unit, fmt) "- os", os_type_name(self%os_type) if (allocated(self%flags)) then write(unit, fmt) "- compiler flags", self%flags @@ -1042,40 +1059,51 @@ logical function profile_same(this,that) select type (other=>that) type is (profile_config_t) + print *, 'check name' if (allocated(this%profile_name).neqv.allocated(other%profile_name)) return if (allocated(this%profile_name)) then if (.not.(this%profile_name==other%profile_name)) return endif + print *, 'check compiler' if (allocated(this%compiler).neqv.allocated(other%compiler)) return if (allocated(this%compiler)) then if (.not.(this%compiler==other%compiler)) return endif + print *, 'check os' if (this%os_type/=other%os_type) return + print *, 'check flags' if (allocated(this%flags).neqv.allocated(other%flags)) return if (allocated(this%flags)) then if (.not.(this%flags==other%flags)) return endif + print *, 'check cflags' if (allocated(this%c_flags).neqv.allocated(other%c_flags)) return if (allocated(this%c_flags)) then if (.not.(this%c_flags==other%c_flags)) return endif + print *, 'check cxxflags' if (allocated(this%cxx_flags).neqv.allocated(other%cxx_flags)) return if (allocated(this%cxx_flags)) then if (.not.(this%cxx_flags==other%cxx_flags)) return endif + print *, 'check link' if (allocated(this%link_time_flags).neqv.allocated(other%link_time_flags)) return if (allocated(this%link_time_flags)) then if (.not.(this%link_time_flags==other%link_time_flags)) return endif + print *, 'check file scope' + if (allocated(this%file_scope_flags).neqv.allocated(other%file_scope_flags)) return if (allocated(this%file_scope_flags)) then if (.not.size(this%file_scope_flags)==size(other%file_scope_flags)) return do ii=1,size(this%file_scope_flags) + print *, 'check ii-th file scope: ',ii if (.not.this%file_scope_flags(ii)==other%file_scope_flags(ii)) return end do endif + print *, 'check builtin' if (this%is_built_in.neqv.other%is_built_in) return class default @@ -1109,7 +1137,8 @@ subroutine profile_dump(self, table, error) if (allocated(error)) return call set_string(table, "compiler", self%compiler, error) if (allocated(error)) return - call set_string(table,"os-type",OS_NAME(self%os_type), error, 'profile_config_t') + print *, 'save os-type = ',os_type_name(self%os_type) + call set_string(table,"os-type",os_type_name(self%os_type), error, 'profile_config_t') if (allocated(error)) return call set_string(table, "flags", self%flags, error) if (allocated(error)) return @@ -1169,8 +1198,52 @@ subroutine profile_load(self, table, error) !> Error handling type(error_t), allocatable, intent(out) :: error -! call get_value(table, "file-name", self%profile_name) -! call get_value(table, "flags", self%flags) + !> Local variables + character(len=:), allocatable :: flag + integer :: ii, jj + type(toml_table), pointer :: ptr_dep, ptr + type(toml_key), allocatable :: keys(:),dep_keys(:) + + call table%get_keys(keys) + + call get_value(table, "profile-name", self%profile_name) + call get_value(table, "compiler", self%compiler) + call get_value(table,"os-type",flag) + print *, 'OS flag = ',flag + call match_os_type(flag, self%os_type) + call get_value(table, "flags", self%flags) + call get_value(table, "c-flags", self%c_flags) + call get_value(table, "cxx-flags", self%cxx_flags) + call get_value(table, "link-time-flags", self%link_time_flags) + call get_value(table, "is-built-in", self%is_built_in, error, 'profile_config_t') + if (allocated(error)) return + + if (allocated(self%file_scope_flags)) deallocate(self%file_scope_flags) + sub_deps: do ii = 1, size(keys) + + select case (keys(ii)%key) + case ("file-scope-flags") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,'profile_config_t: error retrieving file_scope_flags table') + return + end if + + !> Read all packages + call ptr%get_keys(dep_keys) + allocate(self%file_scope_flags(size(dep_keys))) + + do jj = 1, size(dep_keys) + + call get_value(ptr, dep_keys(jj), ptr_dep) + call self%file_scope_flags(jj)%load_from_toml(ptr_dep, error) + if (allocated(error)) return + + end do + + end select + end do sub_deps end subroutine profile_load diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 917fd314fd..676189ccb6 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -517,6 +517,9 @@ subroutine test_profiles(error) return end if + call chosen_profile%test_serialization('profile serialization: '//profile_name//' '//compiler,error) + if (allocated(error)) return + profile_name = 'release' compiler = 'gfortran' call find_profile(package%profiles, profile_name, compiler, 3, profile_found, chosen_profile) @@ -525,6 +528,9 @@ subroutine test_profiles(error) return end if + call chosen_profile%test_serialization('profile serialization: '//profile_name//' '//compiler,error) + if (allocated(error)) return + profile_name = 'publish' compiler = 'gfortran' call find_profile(package%profiles, profile_name, compiler, 1, profile_found, chosen_profile) @@ -533,6 +539,9 @@ subroutine test_profiles(error) return end if + call chosen_profile%test_serialization('profile serialization: '//profile_name//' '//compiler,error) + if (allocated(error)) return + profile_name = 'debug' compiler = 'ifort' call find_profile(package%profiles, profile_name, compiler, 3, profile_found, chosen_profile) @@ -541,6 +550,9 @@ subroutine test_profiles(error) return end if + call chosen_profile%test_serialization('profile serialization: '//profile_name//' '//compiler,error) + if (allocated(error)) return + profile_name = 'release' compiler = 'ifort' call find_profile(package%profiles, profile_name, compiler, 1, profile_found, chosen_profile) @@ -548,6 +560,10 @@ subroutine test_profiles(error) call test_failed(error, "Failed to overwrite built-in profile") return end if + + call chosen_profile%test_serialization('profile serialization: '//profile_name//' '//compiler,error) + if (allocated(error)) return + end subroutine test_profiles !> 'flags' is a key-value entry, test should fail as it is defined as a table From 432cbab11a15eb966c4e9a1a63ce20202efd9bc5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 16:02:08 +0200 Subject: [PATCH 541/799] serialize `package_config_t` --- src/fpm/manifest/package.f90 | 552 ++++++++++++++++++++++++++++++++++- src/fpm_model.f90 | 1 - 2 files changed, 550 insertions(+), 3 deletions(-) diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index db5beba5ed..120344d907 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -46,7 +46,8 @@ module fpm_manifest_package use fpm_manifest_preprocess, only : preprocess_config_t, new_preprocessors use fpm_filesystem, only : exists, getline, join_path use fpm_error, only : error_t, fatal_error, syntax_error, bad_name_error - use fpm_toml, only : toml_table, toml_array, toml_key, toml_stat, get_value, len + use fpm_toml, only : toml_table, toml_array, toml_key, toml_stat, get_value, len, & + serializable_t, set_value, set_string, set_list, add_table use fpm_versioning, only : version_t, new_version implicit none private @@ -61,7 +62,7 @@ module fpm_manifest_package !> Package meta data - type :: package_config_t + type, extends(serializable_t) :: package_config_t !> Name of the package character(len=:), allocatable :: name @@ -110,8 +111,15 @@ module fpm_manifest_package !> Print information on this instance procedure :: info + !> Serialization interface + procedure :: serializable_is_same => manifest_is_same + procedure :: dump_to_toml + procedure :: load_from_toml + end type package_config_t + character(len=*), parameter, private :: class_name = 'package_config_t' + contains @@ -507,5 +515,545 @@ subroutine unique_programs2(executable_i, executable_j, error) end subroutine unique_programs2 + logical function manifest_is_same(this,that) + class(package_config_t), intent(in) :: this + class(serializable_t), intent(in) :: that + + integer :: ii + + manifest_is_same = .false. + + select type (other=>that) + type is (package_config_t) + + if (.not.this%name==other%name) return + if (.not.this%version==other%version) return + if (.not.this%build==other%build) return + if (.not.this%install==other%install) return + if (.not.this%fortran==other%fortran) return + if (.not.this%license==other%license) return + if (allocated(this%library).neqv.allocated(other%library)) return + if (allocated(this%library)) then + if (.not.this%library==other%library) return + endif + if (allocated(this%executable).neqv.allocated(other%executable)) return + if (allocated(this%executable)) then + if (.not.size(this%executable)==size(other%executable)) return + do ii=1,size(this%executable) + if (.not.this%executable(ii)==other%executable(ii)) return + end do + end if + if (allocated(this%dependency).neqv.allocated(other%dependency)) return + if (allocated(this%dependency)) then + if (.not.size(this%dependency)==size(other%dependency)) return + do ii=1,size(this%dependency) + if (.not.this%dependency(ii)==other%dependency(ii)) return + end do + end if + if (allocated(this%dev_dependency).neqv.allocated(other%dev_dependency)) return + if (allocated(this%dev_dependency)) then + if (.not.size(this%dev_dependency)==size(other%dev_dependency)) return + do ii=1,size(this%dev_dependency) + if (.not.this%dev_dependency(ii)==other%dev_dependency(ii)) return + end do + end if + if (allocated(this%profiles).neqv.allocated(other%profiles)) return + if (allocated(this%profiles)) then + if (.not.size(this%profiles)==size(other%profiles)) return + do ii=1,size(this%profiles) + if (.not.this%profiles(ii)==other%profiles(ii)) return + end do + end if + if (allocated(this%example).neqv.allocated(other%example)) return + if (allocated(this%example)) then + if (.not.size(this%example)==size(other%example)) return + do ii=1,size(this%example) + if (.not.this%example(ii)==other%example(ii)) return + end do + end if + if (allocated(this%preprocess).neqv.allocated(other%preprocess)) return + if (allocated(this%preprocess)) then + if (.not.size(this%preprocess)==size(other%preprocess)) return + do ii=1,size(this%preprocess) + if (.not.this%preprocess(ii)==other%preprocess(ii)) return + end do + end if + if (allocated(this%test).neqv.allocated(other%test)) return + if (allocated(this%test)) then + if (.not.size(this%test)==size(other%test)) return + do ii=1,size(this%test) + if (.not.this%test(ii)==other%test(ii)) return + end do + end if + + class default + ! Not the same type + return + end select + + !> All checks passed! + manifest_is_same = .true. + + end function manifest_is_same + + !> Dump manifest to toml table + subroutine dump_to_toml(self, table, error) + + !> Instance of the serializable object + class(package_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: ierr, ii + type(toml_table), pointer :: ptr,ptr_pkg + character(30) :: unnamed + + call set_string(table, "name", self%name, error, class_name) + if (allocated(error)) return + call set_string(table, "version", self%version%s(), error, class_name) + if (allocated(error)) return + call set_string(table, "license", self%license, error, class_name) + if (allocated(error)) return + + call add_table(table, "build", ptr, error, class_name) + if (allocated(error)) return + call self%build%dump_to_toml(ptr, error) + if (allocated(error)) return + + call add_table(table, "fortran", ptr, error, class_name) + if (allocated(error)) return + call self%fortran%dump_to_toml(ptr, error) + if (allocated(error)) return + + call add_table(table, "install", ptr, error, class_name) + if (allocated(error)) return + call self%install%dump_to_toml(ptr, error) + if (allocated(error)) return + + if (allocated(self%library)) then + call add_table(table, "library", ptr, error, class_name) + if (allocated(error)) return + call self%library%dump_to_toml(ptr, error) + if (allocated(error)) return + end if + + if (allocated(self%executable)) then + + call add_table(table, "executable", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'executable' table ") + return + end if + + do ii = 1, size(self%executable) + + associate (pkg => self%executable(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) 'EXECUTABLE',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(executable)') + else + call add_table(ptr_pkg, pkg%name, ptr, error, class_name//'(executable)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + if (allocated(self%dependency)) then + + call add_table(table, "dependencies", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'dependencies' table ") + return + end if + + do ii = 1, size(self%dependency) + + associate (pkg => self%dependency(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) 'DEPENDENCY',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(dependencies)') + else + call add_table(ptr_pkg, pkg%name, ptr, error, class_name//'(dependencies)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + if (allocated(self%dev_dependency)) then + + call add_table(table, "dev-dependencies", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'dev-dependencies' table ") + return + end if + + do ii = 1, size(self%dev_dependency) + + associate (pkg => self%dev_dependency(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) 'DEV-DEPENDENCY',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(dev-dependencies)') + else + call add_table(ptr_pkg, pkg%name, ptr, error, class_name//'(dev-dependencies)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + if (allocated(self%profiles)) then + + call add_table(table, "profiles", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'profiles' table ") + return + end if + + do ii = 1, size(self%profiles) + + associate (pkg => self%profiles(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%profile_name)==0) then + write(unnamed,1) 'PROFILE',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(profiles)') + else + call add_table(ptr_pkg, pkg%profile_name, ptr, error, class_name//'(profiles)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + if (allocated(self%example)) then + + call add_table(table, "example", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'example' table ") + return + end if + + do ii = 1, size(self%example) + + associate (pkg => self%example(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) 'EXAMPLE',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(example)') + else + call add_table(ptr_pkg, pkg%name, ptr, error, class_name//'(example)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + if (allocated(self%test)) then + + call add_table(table, "test", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'test' table ") + return + end if + + do ii = 1, size(self%test) + + associate (pkg => self%test(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) 'TEST',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(test)') + else + call add_table(ptr_pkg, pkg%name, ptr, error, class_name//'(test)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + if (allocated(self%preprocess)) then + + call add_table(table, "preprocess", ptr_pkg) + if (.not. associated(ptr_pkg)) then + call fatal_error(error, class_name//" cannot create 'preprocess' table ") + return + end if + + do ii = 1, size(self%preprocess) + + associate (pkg => self%preprocess(ii)) + + !> Because dependencies are named, fallback if this has no name + !> So, serialization will work regardless of size(self%dep) == self%ndep + if (len_trim(pkg%name)==0) then + write(unnamed,1) 'PREPROCESS',ii + call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(preprocess)') + else + call add_table(ptr_pkg, pkg%name, ptr, error, class_name//'(preprocess)') + end if + if (allocated(error)) return + call pkg%dump_to_toml(ptr, error) + if (allocated(error)) return + + end associate + + end do + end if + + 1 format('UNNAMED_',a,'_',i0) + + end subroutine dump_to_toml + + !> Read manifest from toml table (no checks made at this stage) + subroutine load_from_toml(self, table, error) + + !> Instance of the serializable object + class(package_config_t), intent(inout) :: self + + !> Data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_key), allocatable :: keys(:),pkg_keys(:) + integer :: ierr, ii, jj + character(len=:), allocatable :: flag + type(toml_table), pointer :: ptr,ptr_pkg + + call table%get_keys(keys) + + call get_value(table, "name", self%name) + call get_value(table, "license", self%license) + call get_value(table, "version", flag) + call new_version(self%version, flag, error) + if (allocated(error)) then + error%message = class_name//': version error from TOML table - '//error%message + return + endif + + if (allocated(self%library)) deallocate(self%library) + if (allocated(self%executable)) deallocate(self%executable) + if (allocated(self%dependency)) deallocate(self%dependency) + if (allocated(self%dev_dependency)) deallocate(self%dev_dependency) + if (allocated(self%profiles)) deallocate(self%profiles) + if (allocated(self%example)) deallocate(self%example) + if (allocated(self%test)) deallocate(self%test) + if (allocated(self%preprocess)) deallocate(self%preprocess) + sub_deps: do ii = 1, size(keys) + + select case (keys(ii)%key) + case ("build") + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving '//keys(ii)%key//' table') + return + end if + call self%build%load_from_toml(ptr, error) + if (allocated(error)) return + + case ("install") + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving '//keys(ii)%key//' table') + return + end if + call self%install%load_from_toml(ptr, error) + + case ("fortran") + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving '//keys(ii)%key//' table') + return + end if + call self%fortran%load_from_toml(ptr, error) + + case ("library") + + allocate(self%library) + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving '//keys(ii)%key//' table') + return + end if + call self%library%load_from_toml(ptr, error) + + case ("executable") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving executable table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%executable(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%executable(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case ("dependencies") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving dependency table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%dependency(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%dependency(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case ("dev-dependencies") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving dev-dependencies table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%dev_dependency(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%dev_dependency(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case ("profiles") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving profiles table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%profiles(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%profiles(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case ("example") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving example table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%example(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%example(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case ("test") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving test table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%test(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%test(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case ("preprocess") + + call get_value(table, keys(ii), ptr) + if (.not.associated(ptr)) then + call fatal_error(error,class_name//': error retrieving preprocess table') + return + end if + + !> Read all packages + call ptr%get_keys(pkg_keys) + allocate(self%preprocess(size(pkg_keys))) + + do jj = 1, size(pkg_keys) + call get_value(ptr, pkg_keys(jj), ptr_pkg) + call self%preprocess(jj)%load_from_toml(ptr_pkg, error) + if (allocated(error)) return + end do + + case default + cycle sub_deps + end select + + end do sub_deps + + end subroutine load_from_toml end module fpm_manifest_package diff --git a/src/fpm_model.f90 b/src/fpm_model.f90 index e34f955246..609e84cfec 100644 --- a/src/fpm_model.f90 +++ b/src/fpm_model.f90 @@ -980,7 +980,6 @@ subroutine model_load_from_toml(self, table, error) type(toml_key), allocatable :: keys(:),pkg_keys(:) integer :: ierr, ii, jj type(toml_table), pointer :: ptr,ptr_pkg - character(27) :: unnamed call table%get_keys(keys) From 8023c5367a20b58ef800c35577e472496435e45d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 17:24:40 +0200 Subject: [PATCH 542/799] manifest: bugfix and test serialization --- src/fpm/manifest/build.f90 | 7 +++---- src/fpm/manifest/install.f90 | 2 +- src/fpm/manifest/package.f90 | 13 +++++-------- src/fpm/manifest/profiles.f90 | 11 ----------- test/fpm_test/test_manifest.f90 | 12 +++++++++++- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90 index 4c743927a9..035ea0d51d 100644 --- a/src/fpm/manifest/build.f90 +++ b/src/fpm/manifest/build.f90 @@ -105,9 +105,8 @@ subroutine new_build_config(self, table, package_name, error) if (stat == toml_stat%success) then - ! Boolean value found. Set no custom prefix. This also falls back to - ! key not provided - self%module_prefix = string_t("") + ! Boolean value found. Set no custom prefix. This also falls back to key not provided + if (allocated(self%module_prefix%s)) deallocate(self%module_prefix%s) else @@ -306,7 +305,7 @@ subroutine load_from_toml(self, table, error) call get_value(table, "module-naming", self%module_naming, .false., stat=stat) if (stat == toml_stat%success) then ! Boolean value found. Set no custom prefix. This also falls back to key not provided - self%module_prefix = string_t("") + if (allocated(self%module_prefix%s)) deallocate(self%module_prefix%s) else !> Value found, but not a boolean. Attempt to read a prefix string call get_value(table, "module-naming", self%module_prefix%s) diff --git a/src/fpm/manifest/install.f90 b/src/fpm/manifest/install.f90 index 5c0f46837f..88c3097eb0 100644 --- a/src/fpm/manifest/install.f90 +++ b/src/fpm/manifest/install.f90 @@ -17,7 +17,7 @@ module fpm_manifest_install type, extends(serializable_t) :: install_config_t !> Install library with this project - logical :: library + logical :: library = .false. contains diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index 120344d907..26d7f56ad0 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -611,6 +611,7 @@ subroutine dump_to_toml(self, table, error) integer :: ierr, ii type(toml_table), pointer :: ptr,ptr_pkg character(30) :: unnamed + character(128) :: profile_name call set_string(table, "name", self%name, error, class_name) if (allocated(error)) return @@ -740,14 +741,10 @@ subroutine dump_to_toml(self, table, error) associate (pkg => self%profiles(ii)) - !> Because dependencies are named, fallback if this has no name - !> So, serialization will work regardless of size(self%dep) == self%ndep - if (len_trim(pkg%profile_name)==0) then - write(unnamed,1) 'PROFILE',ii - call add_table(ptr_pkg, trim(unnamed), ptr, error, class_name//'(profiles)') - else - call add_table(ptr_pkg, pkg%profile_name, ptr, error, class_name//'(profiles)') - end if + !> Duplicate profile names are possible, as multiple profiles are possible with the + !> same name, same compiler, etc. So, use a unique name here + write(profile_name,1) 'PROFILE',ii + call add_table(ptr_pkg, trim(profile_name), ptr, error, class_name//'(profiles)') if (allocated(error)) return call pkg%dump_to_toml(ptr, error) if (allocated(error)) return diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 1852118374..951f5bfdab 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -1059,41 +1059,32 @@ logical function profile_same(this,that) select type (other=>that) type is (profile_config_t) - print *, 'check name' if (allocated(this%profile_name).neqv.allocated(other%profile_name)) return if (allocated(this%profile_name)) then if (.not.(this%profile_name==other%profile_name)) return endif - print *, 'check compiler' if (allocated(this%compiler).neqv.allocated(other%compiler)) return if (allocated(this%compiler)) then if (.not.(this%compiler==other%compiler)) return endif - print *, 'check os' if (this%os_type/=other%os_type) return - print *, 'check flags' if (allocated(this%flags).neqv.allocated(other%flags)) return if (allocated(this%flags)) then if (.not.(this%flags==other%flags)) return endif - print *, 'check cflags' if (allocated(this%c_flags).neqv.allocated(other%c_flags)) return if (allocated(this%c_flags)) then if (.not.(this%c_flags==other%c_flags)) return endif - print *, 'check cxxflags' if (allocated(this%cxx_flags).neqv.allocated(other%cxx_flags)) return if (allocated(this%cxx_flags)) then if (.not.(this%cxx_flags==other%cxx_flags)) return endif - print *, 'check link' if (allocated(this%link_time_flags).neqv.allocated(other%link_time_flags)) return if (allocated(this%link_time_flags)) then if (.not.(this%link_time_flags==other%link_time_flags)) return endif - print *, 'check file scope' - if (allocated(this%file_scope_flags).neqv.allocated(other%file_scope_flags)) return if (allocated(this%file_scope_flags)) then if (.not.size(this%file_scope_flags)==size(other%file_scope_flags)) return @@ -1103,7 +1094,6 @@ logical function profile_same(this,that) end do endif - print *, 'check builtin' if (this%is_built_in.neqv.other%is_built_in) return class default @@ -1209,7 +1199,6 @@ subroutine profile_load(self, table, error) call get_value(table, "profile-name", self%profile_name) call get_value(table, "compiler", self%compiler) call get_value(table,"os-type",flag) - print *, 'OS flag = ',flag call match_os_type(flag, self%os_type) call get_value(table, "flags", self%flags) call get_value(table, "c-flags", self%c_flags) diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index 676189ccb6..58f6ad7e22 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -167,6 +167,10 @@ subroutine test_valid_manifest(error) return end if + ! Test package serialization + call package%test_serialization('test_valid_manifest',error) + if (allocated(error)) return + end subroutine test_valid_manifest @@ -220,6 +224,9 @@ subroutine test_default_library(error) return end if + call package%test_serialization('test_default_library',error) + if (allocated(error)) return + end subroutine test_default_library @@ -243,6 +250,9 @@ subroutine test_default_executable(error) & "Default executable name") if (allocated(error)) return + call package%test_serialization('test_default_executable',error) + if (allocated(error)) return + end subroutine test_default_executable @@ -1253,7 +1263,7 @@ subroutine test_link_array(error) if (allocated(error)) return !> Test serialization roundtrip - call build%test_serialization('test_link_string', error) + call build%test_serialization('test_link_array', error) if (allocated(error)) return end subroutine test_link_array From bcba0e133b914ba6bdd55c323813bf1f35d22481 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 17:35:49 +0200 Subject: [PATCH 543/799] fix `version_t` bug --- src/fpm/versioning.f90 | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/fpm/versioning.f90 b/src/fpm/versioning.f90 index 4c7c01712a..6f32a183a9 100644 --- a/src/fpm/versioning.f90 +++ b/src/fpm/versioning.f90 @@ -227,15 +227,17 @@ pure function s(self) result(string) character(len=buffersize) :: buffer integer :: ii - do ii = 1, size(self%num) - if (allocated(string)) then - write(buffer, '(".", i0)') self%num(ii) - string = string // trim(buffer) - else - write(buffer, '(i0)') self%num(ii) - string = trim(buffer) - end if - end do + if (allocated(self%num)) then + do ii = 1, size(self%num) + if (allocated(string)) then + write(buffer, '(".", i0)') self%num(ii) + string = string // trim(buffer) + else + write(buffer, '(i0)') self%num(ii) + string = trim(buffer) + end if + end do + endif if (.not.allocated(string)) then string = '0' From 71a94633568e3a4af0235e84b1cac63c133d465f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 17:42:20 +0200 Subject: [PATCH 544/799] fix more `version_t` bound errors --- src/fpm/versioning.f90 | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/fpm/versioning.f90 b/src/fpm/versioning.f90 index 6f32a183a9..d8f4f46432 100644 --- a/src/fpm/versioning.f90 +++ b/src/fpm/versioning.f90 @@ -227,17 +227,15 @@ pure function s(self) result(string) character(len=buffersize) :: buffer integer :: ii - if (allocated(self%num)) then - do ii = 1, size(self%num) - if (allocated(string)) then - write(buffer, '(".", i0)') self%num(ii) - string = string // trim(buffer) - else - write(buffer, '(i0)') self%num(ii) - string = trim(buffer) - end if - end do - endif + do ii = 1, ndigits(self) + if (allocated(string)) then + write(buffer, '(".", i0)') self%num(ii) + string = string // trim(buffer) + else + write(buffer, '(i0)') self%num(ii) + string = trim(buffer) + end if + end do if (.not.allocated(string)) then string = '0' @@ -298,18 +296,18 @@ elemental function greater(lhs, rhs) result(is_greater) !> First version is greater logical :: is_greater - integer :: ii + integer :: ii, lhs_size, rhs_size - do ii = 1, min(size(lhs%num), size(rhs%num)) + do ii = 1, min(ndigits(lhs),ndigits(rhs)) if (lhs%num(ii) /= rhs%num(ii)) then is_greater = lhs%num(ii) > rhs%num(ii) return end if end do - is_greater = size(lhs%num) > size(rhs%num) + is_greater = ndigits(lhs) > ndigits(rhs) if (is_greater) then - do ii = size(rhs%num) + 1, size(lhs%num) + do ii = ndigits(rhs) + 1, ndigits(lhs) is_greater = lhs%num(ii) > 0 if (is_greater) return end do @@ -392,5 +390,17 @@ elemental function match(lhs, rhs) end function match + !> Number of digits + elemental integer function ndigits(self) + class(version_t), intent(in) :: self + + if (allocated(self%num)) then + ndigits = size(self%num) + else + ndigits = 0 + end if + + end function ndigits + end module fpm_versioning From 2990bc726929d85c3a35b1da95e41b1e2f47e62a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 17:42:28 +0200 Subject: [PATCH 545/799] cleanup --- src/fpm/manifest/profiles.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 951f5bfdab..6b139910d9 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -1127,7 +1127,6 @@ subroutine profile_dump(self, table, error) if (allocated(error)) return call set_string(table, "compiler", self%compiler, error) if (allocated(error)) return - print *, 'save os-type = ',os_type_name(self%os_type) call set_string(table,"os-type",os_type_name(self%os_type), error, 'profile_config_t') if (allocated(error)) return call set_string(table, "flags", self%flags, error) From 170070b2268fa0c6986d3036b99ed672b32505a2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 18:36:17 +0200 Subject: [PATCH 546/799] deploy `fpm export` --- app/main.f90 | 4 ++ src/fpm/cmd/export.f90 | 83 ++++++++++++++++++++++++++++++++++++ src/fpm/cmd/update.f90 | 2 +- src/fpm/manifest/package.f90 | 3 +- src/fpm_command_line.f90 | 47 ++++++++++++++++++-- 5 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 src/fpm/cmd/export.f90 diff --git a/app/main.f90 b/app/main.f90 index 95df065097..4bd3ac5e33 100644 --- a/app/main.f90 +++ b/app/main.f90 @@ -4,6 +4,7 @@ program main fpm_cmd_settings, & fpm_new_settings, & fpm_build_settings, & + fpm_export_settings, & fpm_run_settings, & fpm_test_settings, & fpm_install_settings, & @@ -15,6 +16,7 @@ program main use fpm_filesystem, only: exists, parent_dir, join_path use fpm, only: cmd_build, cmd_run, cmd_clean use fpm_cmd_install, only: cmd_install +use fpm_cmd_export, only: cmd_export use fpm_cmd_new, only: cmd_new use fpm_cmd_update, only : cmd_update use fpm_cmd_publish, only: cmd_publish @@ -76,6 +78,8 @@ program main call cmd_run(settings,test=.false.) type is (fpm_test_settings) call cmd_run(settings,test=.true.) +type is (fpm_export_settings) + call cmd_export(settings) type is (fpm_install_settings) call cmd_install(settings) type is (fpm_update_settings) diff --git a/src/fpm/cmd/export.f90 b/src/fpm/cmd/export.f90 new file mode 100644 index 0000000000..6386c8853a --- /dev/null +++ b/src/fpm/cmd/export.f90 @@ -0,0 +1,83 @@ +module fpm_cmd_export + use fpm_command_line, only : fpm_export_settings + use fpm_dependency, only : dependency_tree_t, new_dependency_tree + use fpm_error, only : error_t, fpm_stop + use fpm_filesystem, only : join_path + use fpm_manifest, only : package_config_t, get_package_data + use fpm_toml, only: name_is_json + use fpm_model, only: fpm_model_t + use fpm, only: build_model + implicit none + private + public :: cmd_export + +contains + + !> Entry point for the export subcommand + subroutine cmd_export(settings) + !> Representation of the command line arguments + type(fpm_export_settings), intent(in) :: settings + type(package_config_t) :: package + type(dependency_tree_t) :: deps + type(fpm_model_t) :: model + type(error_t), allocatable :: error + + integer :: ii + character(len=:), allocatable :: filename + + if (len_trim(settings%dump_manifest)<=0 .and. & + len_trim(settings%dump_model)<=0 .and. & + len_trim(settings%dump_dependencies)<=0) then + call fpm_stop(0,'*cmd_export* exiting: no manifest/model/dependencies keyword provided') + end if + + !> Read in manifest + call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) + call handle_error(error) + + !> Export manifest + if (len_trim(settings%dump_manifest)>0) then + filename = trim(settings%dump_manifest) + call package%dump(filename, error, json=name_is_json(filename)) + end if + + !> Export dependency tree + if (len_trim(settings%dump_dependencies)>0) then + + !> Generate dependency tree + filename = join_path("build", "cache.toml") + call new_dependency_tree(deps, cache=filename, verbosity=merge(2, 1, settings%verbose)) + call deps%add(package, error) + call handle_error(error) + + !> Export dependency tree + filename = settings%dump_dependencies + call deps%dump(filename, error, json=name_is_json(filename)) + call handle_error(error) + end if + + !> Export full model + if (len_trim(settings%dump_model)>0) then + + call build_model(model, settings%fpm_build_settings, package, error) + if (allocated(error)) then + call fpm_stop(1,'*cmd_export* Model error: '//error%message) + end if + + filename = settings%dump_model + call model%dump(filename, error, json=name_is_json(filename)) + call handle_error(error) + end if + + end subroutine cmd_export + + !> Error handling for this command + subroutine handle_error(error) + !> Potential error + type(error_t), intent(in), optional :: error + if (present(error)) then + call fpm_stop(1, '*cmd_export* error: '//error%message) + end if + end subroutine handle_error + +end module fpm_cmd_export diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index 11ca717441..75353c6a6e 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -15,10 +15,10 @@ module fpm_cmd_update subroutine cmd_update(settings) !> Representation of the command line arguments type(fpm_update_settings), intent(in) :: settings + type(package_config_t) :: package type(dependency_tree_t) :: deps type(error_t), allocatable :: error - integer :: ii character(len=:), allocatable :: cache diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index 26d7f56ad0..3fca2a48ab 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -743,7 +743,7 @@ subroutine dump_to_toml(self, table, error) !> Duplicate profile names are possible, as multiple profiles are possible with the !> same name, same compiler, etc. So, use a unique name here - write(profile_name,1) 'PROFILE',ii + write(profile_name,2) ii call add_table(ptr_pkg, trim(profile_name), ptr, error, class_name//'(profiles)') if (allocated(error)) return call pkg%dump_to_toml(ptr, error) @@ -842,6 +842,7 @@ subroutine dump_to_toml(self, table, error) end if 1 format('UNNAMED_',a,'_',i0) + 2 format('PROFILE_',i0) end subroutine dump_to_toml diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 8cd4776b75..6b545b5dce 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -44,6 +44,7 @@ module fpm_command_line public :: fpm_cmd_settings, & fpm_build_settings, & fpm_install_settings, & + fpm_export_settings, & fpm_new_settings, & fpm_run_settings, & fpm_test_settings, & @@ -108,9 +109,16 @@ module fpm_command_line !> Settings for interacting and updating with project dependencies type, extends(fpm_cmd_settings) :: fpm_update_settings character(len=ibug),allocatable :: name(:) - character(len=:),allocatable :: dump - logical :: fetch_only - logical :: clean + character(len=:),allocatable :: dump + logical :: fetch_only + logical :: clean +end type + +!> Settings for exporting model data +type, extends(fpm_build_settings) :: fpm_export_settings + character(len=:),allocatable :: dump_manifest + character(len=:),allocatable :: dump_dependencies + character(len=:),allocatable :: dump_model end type type, extends(fpm_cmd_settings) :: fpm_clean_settings @@ -221,6 +229,7 @@ subroutine get_command_line_settings(cmd_settings) logical :: is_unix type(fpm_install_settings), allocatable :: install_settings type(fpm_publish_settings), allocatable :: publish_settings + type(fpm_export_settings) , allocatable :: export_settings type(version_t) :: version character(len=:), allocatable :: common_args, compiler_args, run_args, working_dir, & & c_compiler, cxx_compiler, archiver, version_s @@ -606,6 +615,38 @@ subroutine get_command_line_settings(cmd_settings) fetch_only=lget('fetch-only'), verbose=lget('verbose'), & clean=lget('clean')) + case('export') + + call set_args(common_args // compiler_args // '& + & --manifest "filename" & + & --model "filename" & + & --dependencies "filename" ', & + help_build, version_text) + + call check_build_vals() + + c_compiler = sget('c-compiler') + cxx_compiler = sget('cxx-compiler') + archiver = sget('archiver') + allocate(export_settings, source=fpm_export_settings(& + profile=val_profile,& + prune=.not.lget('no-prune'), & + compiler=val_compiler, & + c_compiler=c_compiler, & + cxx_compiler=cxx_compiler, & + archiver=archiver, & + flag=val_flag, & + cflag=val_cflag, & + show_model=.true., & + cxxflag=val_cxxflag, & + ldflag=val_ldflag, & + verbose=lget('verbose'))) + call get_char_arg(export_settings%dump_model, 'model') + call get_char_arg(export_settings%dump_manifest, 'manifest') + call get_char_arg(export_settings%dump_dependencies, 'dependencies') + call move_alloc(export_settings, cmd_settings) + + case('clean') call set_args(common_args // & & ' --skip' // & From ba27a3b785cfcf0531e2018a18f3bebcb42e8ad2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 18 May 2023 20:26:01 +0200 Subject: [PATCH 547/799] store `author`, `maintainer`, `copyright` metadata --- src/fpm/manifest/package.f90 | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/fpm/manifest/package.f90 b/src/fpm/manifest/package.f90 index 3fca2a48ab..57ab93110c 100644 --- a/src/fpm/manifest/package.f90 +++ b/src/fpm/manifest/package.f90 @@ -82,6 +82,15 @@ module fpm_manifest_package !> License meta data character(len=:), allocatable :: license + !> Author meta data + character(len=:), allocatable :: author + + !> Maintainer meta data + character(len=:), allocatable :: maintainer + + !> Copyright meta data + character(len=:), allocatable :: copyright + !> Library meta data type(library_config_t), allocatable :: library @@ -161,6 +170,9 @@ subroutine new_package(self, table, root, error) endif call get_value(table, "license", self%license) + call get_value(table, "author", self%author) + call get_value(table, "maintainer", self%maintainer) + call get_value(table, "copyright", self%copyright) if (len(self%name) <= 0) then call syntax_error(error, "Package name must be a non-empty string") @@ -532,6 +544,9 @@ logical function manifest_is_same(this,that) if (.not.this%install==other%install) return if (.not.this%fortran==other%fortran) return if (.not.this%license==other%license) return + if (.not.this%author==other%author) return + if (.not.this%maintainer==other%maintainer) return + if (.not.this%copyright==other%copyright) return if (allocated(this%library).neqv.allocated(other%library)) return if (allocated(this%library)) then if (.not.this%library==other%library) return @@ -619,6 +634,12 @@ subroutine dump_to_toml(self, table, error) if (allocated(error)) return call set_string(table, "license", self%license, error, class_name) if (allocated(error)) return + call set_string(table, "author", self%author, error, class_name) + if (allocated(error)) return + call set_string(table, "maintainer", self%maintainer, error, class_name) + if (allocated(error)) return + call set_string(table, "copyright", self%copyright, error, class_name) + if (allocated(error)) return call add_table(table, "build", ptr, error, class_name) if (allocated(error)) return @@ -867,6 +888,9 @@ subroutine load_from_toml(self, table, error) call get_value(table, "name", self%name) call get_value(table, "license", self%license) + call get_value(table, "author", self%author) + call get_value(table, "maintainer", self%maintainer) + call get_value(table, "copyright", self%copyright) call get_value(table, "version", flag) call new_version(self%version, flag, error) if (allocated(error)) then From 40b0c355a740dd261cf2f65f53ebb884bd052ef9 Mon Sep 17 00:00:00 2001 From: Minh Dao <43783196+minhqdao@users.noreply.github.com> Date: Fri, 19 May 2023 15:11:53 +0700 Subject: [PATCH 548/799] Return char* instead of int (#914) Co-authored-by: minhqdao --- src/fpm_os.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_os.c b/src/fpm_os.c index 2d417a0695..49e1a4d5f4 100644 --- a/src/fpm_os.c +++ b/src/fpm_os.c @@ -5,7 +5,7 @@ /// @param resolved_path /// @param maxLength /// @return -int c_realpath(char* path, char* resolved_path, int maxLength) { +char* c_realpath(char* path, char* resolved_path, int maxLength) { // Checking macro in C because it doesn't work with gfortran on Windows, even // when exported manually. #ifndef _WIN32 From a3d689fb6f319afb09987bda1d0e59f66d71e9e0 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 21 May 2023 03:36:05 -0500 Subject: [PATCH 549/799] Fix failing tests with Intel compiler (#901) * error #7976: An allocatable dummy argument may only be argument associated with an allocatable actual argument. [S] * enforce Fortran standard to enable LHS reallocation * fix empty args * fix input namelist formats * fix SEGFAULT building fpm_publish_settings * Revert "fix SEGFAULT building fpm_publish_settings" This reverts commit e0c86d64f2af32b63d8c4790feca62a4506e30ac. * Revert "Revert "fix SEGFAULT building fpm_publish_settings"" This reverts commit aca4925c856afa6244b1c5f712225956490883aa. * Revert "fix empty args" This reverts commit 8f1a8f3ab28a988e7dcbe059c4bce658363af9ad. * fix test-manifest routine (segfault unallocated `flags`) * line too long * Revert "Revert "fix empty args"" This reverts commit 3d2907bc36a9cff28074c9df8deff804f380adaa. * Revert "Revert "Revert "fix SEGFAULT building fpm_publish_settings""" This reverts commit ff1e885ef7104c89b261ff3111f0fb31607cecf0. * make fpm_publish_settings work with both gfortran and intel * Update fpm_command_line.f90 * fix bus error returning string * fix unallocated variables in non-allocatable dummy arguments * fix more unallocated strings * check existing directory: intel compiler fix * fix join_path in dependency with root specified * more unallocated strings * fix ifort bug with extended `mock_dependency_tree_t` --- src/fpm/dependency.f90 | 35 ++++++------ src/fpm/git.f90 | 7 ++- src/fpm/manifest/dependency.f90 | 4 +- src/fpm/manifest/profiles.f90 | 60 ++++++++++----------- src/fpm_command_line.f90 | 12 ++--- src/fpm_compiler.F90 | 29 +++++++--- src/fpm_filesystem.F90 | 6 +++ src/fpm_settings.f90 | 30 ++++++++--- src/fpm_source_parsing.f90 | 14 ++--- src/fpm_sources.f90 | 19 +++++++ src/fpm_targets.f90 | 14 ++--- test/cli_test/cli_test.f90 | 20 +++---- test/fpm_test/test_manifest.f90 | 25 +++++---- test/fpm_test/test_os.f90 | 3 ++ test/fpm_test/test_package_dependencies.f90 | 29 +++++++++- 15 files changed, 201 insertions(+), 106 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 8beb8ae0db..600c43fdb2 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -719,40 +719,45 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) integer :: code, stat type(json_object), pointer :: p, q - character(:), allocatable :: version_key, version_str, error_message + character(:), allocatable :: version_key, version_str, error_message, namespace, name + + namespace = "" + name = "UNNAMED_NODE" + if (allocated(node%namespace)) namespace = node%namespace + if (allocated(node%name)) name = node%name if (.not. json%has_key('code')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No status code."); return + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': No status code."); return end if call get_value(json, 'code', code, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "// & + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': "// & & "Failed to read status code."); return end if if (code /= 200) then if (.not. json%has_key('message')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No error message."); return + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': No error message."); return end if call get_value(json, 'message', error_message, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': "// & + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': "// & & "Failed to read error message."); return end if - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"'. Status code: '"// & + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"'. Status code: '"// & & str(code)//"'. Error message: '"//error_message//"'."); return end if if (.not. json%has_key('data')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No data."); return + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': No data."); return end if call get_value(json, 'data', p, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to read package data for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to read package data for '"//join_path(namespace, name)//"'."); return end if if (allocated(node%requested_version)) then @@ -762,38 +767,38 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) end if if (.not. p%has_key(version_key)) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version data."); return + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': No version data."); return end if call get_value(p, version_key, q, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to retrieve version data for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to retrieve version data for '"//join_path(namespace, name)//"'."); return end if if (.not. q%has_key('download_url')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No download url."); return + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': No download url."); return end if call get_value(q, 'download_url', download_url, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to read download url for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to read download url for '"//join_path(namespace, name)//"'."); return end if download_url = official_registry_base_url//download_url if (.not. q%has_key('version')) then - call fatal_error(error, "Failed to download '"//join_path(node%namespace, node%name)//"': No version found."); return + call fatal_error(error, "Failed to download '"//join_path(namespace, name)//"': No version found."); return end if call get_value(q, 'version', version_str, stat=stat) if (stat /= 0) then - call fatal_error(error, "Failed to read version data for '"//join_path(node%namespace, node%name)//"'."); return + call fatal_error(error, "Failed to read version data for '"//join_path(namespace, name)//"'."); return end if call new_version(version, version_str, error) if (allocated(error)) then call fatal_error(error, "'"//version_str//"' is not a valid version for '"// & - & join_path(node%namespace, node%name)//"'."); return + & join_path(namespace, name)//"'."); return end if end subroutine diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 602516ea74..b1cd1d8376 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -5,7 +5,10 @@ module fpm_git implicit none public :: git_target_t, git_target_default, git_target_branch, git_target_tag, git_target_revision, git_revision, & - & git_archive, git_matches_manifest, operator(==) + & git_archive, git_matches_manifest, operator(==), compressed_package_name + + !> Name of the compressed package that is generated temporarily. + character(len=*), parameter :: compressed_package_name = 'compressed_package' !> Possible git target type :: enum_descriptor @@ -162,6 +165,8 @@ logical function git_matches_manifest(cached,manifest,verbosity,iunit) !> while the cached dependency always stores a commit hash because it's built !> after the repo is available (saved as git_descriptor%revision==revision). !> So, comparing against the descriptor is not reliable + git_matches_manifest = allocated(cached%object) .eqv. allocated(manifest%object) + if (git_matches_manifest .and. allocated(cached%object)) & git_matches_manifest = cached%object == manifest%object if (.not.git_matches_manifest) then if (verbosity>1) write(iunit,out_fmt) "GIT OBJECT has changed: ",cached%object," vs. ", manifest%object diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 1ca53bc9cf..3d8f38d840 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -27,7 +27,7 @@ module fpm_manifest_dependency use fpm_git, only: git_target_t, git_target_tag, git_target_branch, & & git_target_revision, git_target_default, operator(==), git_matches_manifest use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys - use fpm_filesystem, only: windows_path + use fpm_filesystem, only: windows_path, join_path use fpm_environment, only: get_os_type, OS_WINDOWS use fpm_versioning, only: version_t, new_version implicit none @@ -94,7 +94,7 @@ subroutine new_dependency(self, table, root, error) call get_value(table, "path", uri) if (allocated(uri)) then if (get_os_type() == OS_WINDOWS) uri = windows_path(uri) - if (present(root)) uri = root//uri ! Relative to the fpm.toml it’s written in + if (present(root)) uri = join_path(root,uri) ! Relative to the fpm.toml it’s written in call move_alloc(uri, self%path) return end if diff --git a/src/fpm/manifest/profiles.f90 b/src/fpm/manifest/profiles.f90 index 2e84f0c6e9..8f1e82eaa5 100644 --- a/src/fpm/manifest/profiles.f90 +++ b/src/fpm/manifest/profiles.f90 @@ -53,7 +53,7 @@ module fpm_manifest_profile & info_profile, find_profile, DEFAULT_COMPILER !> Name of the default compiler - character(len=*), parameter :: DEFAULT_COMPILER = 'gfortran' + character(len=*), parameter :: DEFAULT_COMPILER = 'gfortran' integer, parameter :: OS_ALL = -1 character(len=:), allocatable :: path @@ -78,7 +78,7 @@ module fpm_manifest_profile !> Value repesenting OS integer :: os_type - + !> Fortran compiler flags character(len=:), allocatable :: flags @@ -110,16 +110,16 @@ module fpm_manifest_profile function new_profile(profile_name, compiler, os_type, flags, c_flags, cxx_flags, & link_time_flags, file_scope_flags, is_built_in) & & result(profile) - + !> Name of the profile character(len=*), intent(in) :: profile_name - + !> Name of the compiler character(len=*), intent(in) :: compiler - + !> Type of the OS integer, intent(in) :: os_type - + !> Fortran compiler flags character(len=*), optional, intent(in) :: flags @@ -190,7 +190,7 @@ subroutine validate_compiler_name(compiler_name, is_valid) is_valid = .false. end select end subroutine validate_compiler_name - + !> Check if os_name is a valid name of a supported OS subroutine validate_os_name(os_name, is_valid) @@ -373,10 +373,10 @@ subroutine get_flags(profile_name, compiler_name, os_type, key_list, table, prof & flags, c_flags, cxx_flags, link_time_flags, file_scope_flags) profindex = profindex + 1 end subroutine get_flags - + !> Traverse operating system tables to obtain number of profiles subroutine traverse_oss_for_size(profile_name, compiler_name, os_list, table, profiles_size, error) - + !> Name of profile character(len=:), allocatable, intent(in) :: profile_name @@ -447,7 +447,7 @@ end subroutine traverse_oss_for_size !> Traverse operating system tables to obtain profiles subroutine traverse_oss(profile_name, compiler_name, os_list, table, profiles, profindex, error) - + !> Name of profile character(len=:), allocatable, intent(in) :: profile_name @@ -468,7 +468,7 @@ subroutine traverse_oss(profile_name, compiler_name, os_list, table, profiles, p !> Index in the list of profiles integer, intent(inout) :: profindex - + type(toml_key), allocatable :: key_list(:) character(len=:), allocatable :: os_name, l_os_name type(toml_table), pointer :: os_node @@ -513,7 +513,7 @@ end subroutine traverse_oss !> Traverse compiler tables subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_size, profiles, profindex) - + !> Name of profile character(len=:), allocatable, intent(in) :: profile_name @@ -522,10 +522,10 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si !> Table containing compiler tables type(toml_table), pointer, intent(in) :: table - + !> Error handling type(error_t), allocatable, intent(out) :: error - + !> Number of profiles in list of profiles integer, intent(inout), optional :: profiles_size @@ -534,8 +534,8 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si !> Index in the list of profiles integer, intent(inout), optional :: profindex - - character(len=:), allocatable :: compiler_name + + character(len=:), allocatable :: compiler_name type(toml_table), pointer :: comp_node type(toml_key), allocatable :: os_list(:) integer :: icomp, stat @@ -544,7 +544,7 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si if (size(comp_list)<1) return do icomp = 1, size(comp_list) call validate_compiler_name(comp_list(icomp)%key, is_valid) - if (is_valid) then + if (is_valid) then compiler_name = comp_list(icomp)%key call get_value(table, compiler_name, comp_node, stat=stat) if (stat /= toml_stat%success) then @@ -567,7 +567,7 @@ subroutine traverse_compilers(profile_name, comp_list, table, error, profiles_si else call fatal_error(error,'*traverse_compilers*:Error: Compiler name not specified or invalid.') end if - end do + end do end subroutine traverse_compilers !> Construct new profiles array from a TOML data structure @@ -596,9 +596,9 @@ subroutine new_profiles(profiles, table, error) default_profiles = get_default_profiles(error) if (allocated(error)) return call table%get_keys(prof_list) - + if (size(prof_list) < 1) return - + profiles_size = 0 do iprof = 1, size(prof_list) @@ -633,7 +633,7 @@ subroutine new_profiles(profiles, table, error) profiles_size = profiles_size + size(default_profiles) allocate(profiles(profiles_size)) - + do profindex=1, size(default_profiles) profiles(profindex) = default_profiles(profindex) end do @@ -719,25 +719,25 @@ function get_default_profiles(error) result(default_profiles) & 'ifort', & & OS_ALL, & & flags = ' -fp-model precise -pc64 -align all -error-limit 1 -reentrancy& - & threaded -nogen-interfaces -assume byterecl', & + & threaded -nogen-interfaces -assume byterecl -standard-semantics', & & is_built_in=.true.), & & new_profile('release', & & 'ifort', & & OS_WINDOWS, & & flags = ' /fp:precise /align:all /error-limit:1 /reentrancy:threaded& - & /nogen-interfaces /assume:byterecl', & + & /nogen-interfaces /assume:byterecl /standard-semantics', & & is_built_in=.true.), & & new_profile('release', & & 'ifx', & & OS_ALL, & & flags = ' -fp-model=precise -pc64 -align all -error-limit 1 -reentrancy& - & threaded -nogen-interfaces -assume byterecl', & + & threaded -nogen-interfaces -assume byterecl -standard-semantics', & & is_built_in=.true.), & & new_profile('release', & & 'ifx', & & OS_WINDOWS, & & flags = ' /fp:precise /align:all /error-limit:1 /reentrancy:threaded& - & /nogen-interfaces /assume:byterecl', & + & /nogen-interfaces /assume:byterecl /standard-semantics', & & is_built_in=.true.), & & new_profile('release', & &'nagfor', & @@ -775,28 +775,28 @@ function get_default_profiles(error) result(default_profiles) & new_profile('debug', & & 'ifort', & & OS_ALL, & - & flags = ' -warn all -check all -error-limit 1 -O0 -g -assume byterecl -traceback', & + & flags = ' -warn all -check all -error-limit 1 -O0 -g -assume byterecl -standard-semantics -traceback', & & is_built_in=.true.), & & new_profile('debug', & & 'ifort', & & OS_WINDOWS, & & flags = ' /warn:all /check:all /error-limit:1& - & /Od /Z7 /assume:byterecl /traceback', & + & /Od /Z7 /assume:byterecl /standard-semantics /traceback', & & is_built_in=.true.), & & new_profile('debug', & & 'ifx', & & OS_ALL, & - & flags = ' -warn all -check all -error-limit 1 -O0 -g -assume byterecl -traceback', & + & flags = ' -warn all -check all -error-limit 1 -O0 -g -assume byterecl -standard-semantics -traceback', & & is_built_in=.true.), & & new_profile('debug', & & 'ifx', & & OS_WINDOWS, & - & flags = ' /warn:all /check:all /error-limit:1 /Od /Z7 /assume:byterecl', & + & flags = ' /warn:all /check:all /error-limit:1 /Od /Z7 /assume:byterecl /standard-semantics', & & is_built_in=.true.), & & new_profile('debug', & & 'ifx', & & OS_WINDOWS, & - & flags = ' /warn:all /check:all /error-limit:1 /Od /Z7 /assume:byterecl', & + & flags = ' /warn:all /check:all /error-limit:1 /Od /Z7 /assume:byterecl /standard-semantics', & & is_built_in=.true.), & & new_profile('debug', & & 'lfortran', & diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 2601b5c63f..f7a0b1380d 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -218,10 +218,9 @@ subroutine get_command_line_settings(cmd_settings) integer :: os logical :: is_unix type(fpm_install_settings), allocatable :: install_settings - type(fpm_publish_settings), allocatable :: publish_settings type(version_t) :: version character(len=:), allocatable :: common_args, compiler_args, run_args, working_dir, & - & c_compiler, cxx_compiler, archiver, version_s + & c_compiler, cxx_compiler, archiver, version_s, token_s character(len=*), parameter :: fc_env = "FC", cc_env = "CC", ar_env = "AR", & & fflags_env = "FFLAGS", cflags_env = "CFLAGS", cxxflags_env = "CXXFLAGS", ldflags_env = "LDFLAGS", & @@ -633,8 +632,10 @@ subroutine get_command_line_settings(cmd_settings) c_compiler = sget('c-compiler') cxx_compiler = sget('cxx-compiler') archiver = sget('archiver') + token_s = sget('token') - allocate(publish_settings, source=fpm_publish_settings( & + allocate(fpm_publish_settings :: cmd_settings) + cmd_settings = fpm_publish_settings( & & show_package_version = lget('show-package-version'), & & show_form_data = lget('show-form-data'), & & profile=val_profile,& @@ -650,9 +651,8 @@ subroutine get_command_line_settings(cmd_settings) & list=lget('list'),& & show_model=lget('show-model'),& & build_tests=lget('tests'),& - & verbose=lget('verbose'))) - call get_char_arg(publish_settings%token, 'token') - call move_alloc(publish_settings, cmd_settings) + & verbose=lget('verbose'),& + & token=token_s) case default diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 80edd73620..c093001e42 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -176,7 +176,8 @@ module fpm_compiler flag_intel_nogen = " -nogen-interfaces", & flag_intel_byterecl = " -assume byterecl", & flag_intel_free_form = " -free", & - flag_intel_fixed_form = " -fixed" + flag_intel_fixed_form = " -fixed", & + flag_intel_standard_compliance = " -standard-semantics" character(*), parameter :: & flag_intel_backtrace_win = " /traceback", & @@ -190,7 +191,8 @@ module fpm_compiler flag_intel_nogen_win = " /nogen-interfaces", & flag_intel_byterecl_win = " /assume:byterecl", & flag_intel_free_form_win = " /free", & - flag_intel_fixed_form_win = " /fixed" + flag_intel_fixed_form_win = " /fixed", & + flag_intel_standard_compliance_win = " /standard-semantics" character(*), parameter :: & flag_nag_coarray = " -coarray=single", & @@ -276,7 +278,8 @@ subroutine get_release_compile_flags(id, flags) flag_intel_limit//& flag_intel_pthread//& flag_intel_nogen//& - flag_intel_byterecl + flag_intel_byterecl//& + flag_intel_standard_compliance case(id_intel_classic_mac) flags = & @@ -285,7 +288,8 @@ subroutine get_release_compile_flags(id, flags) flag_intel_limit//& flag_intel_pthread//& flag_intel_nogen//& - flag_intel_byterecl + flag_intel_byterecl//& + flag_intel_standard_compliance case(id_intel_classic_windows) flags = & @@ -294,7 +298,8 @@ subroutine get_release_compile_flags(id, flags) flag_intel_limit_win//& flag_intel_pthread_win//& flag_intel_nogen_win//& - flag_intel_byterecl_win + flag_intel_byterecl_win//& + flag_intel_standard_compliance_win case(id_intel_llvm_nix) flags = & @@ -303,7 +308,8 @@ subroutine get_release_compile_flags(id, flags) flag_intel_limit//& flag_intel_pthread//& flag_intel_nogen//& - flag_intel_byterecl + flag_intel_byterecl//& + flag_intel_standard_compliance case(id_intel_llvm_windows) flags = & @@ -312,7 +318,8 @@ subroutine get_release_compile_flags(id, flags) flag_intel_limit_win//& flag_intel_pthread_win//& flag_intel_nogen_win//& - flag_intel_byterecl_win + flag_intel_byterecl_win//& + flag_intel_standard_compliance_win case(id_nag) flags = & @@ -376,7 +383,9 @@ subroutine get_debug_compile_flags(id, flags) flag_intel_limit//& flag_intel_debug//& flag_intel_byterecl//& + flag_intel_standard_compliance//& flag_intel_backtrace + case(id_intel_classic_mac) flags = & flag_intel_warn//& @@ -384,6 +393,7 @@ subroutine get_debug_compile_flags(id, flags) flag_intel_limit//& flag_intel_debug//& flag_intel_byterecl//& + flag_intel_standard_compliance//& flag_intel_backtrace case(id_intel_classic_windows) flags = & @@ -392,6 +402,7 @@ subroutine get_debug_compile_flags(id, flags) flag_intel_limit_win//& flag_intel_debug_win//& flag_intel_byterecl_win//& + flag_intel_standard_compliance_win//& flag_intel_backtrace_win case(id_intel_llvm_nix) flags = & @@ -400,6 +411,7 @@ subroutine get_debug_compile_flags(id, flags) flag_intel_limit//& flag_intel_debug//& flag_intel_byterecl//& + flag_intel_standard_compliance//& flag_intel_backtrace case(id_intel_llvm_windows) flags = & @@ -407,7 +419,8 @@ subroutine get_debug_compile_flags(id, flags) flag_intel_check_win//& flag_intel_limit_win//& flag_intel_debug_win//& - flag_intel_byterecl_win + flag_intel_byterecl_win//& + flag_intel_standard_compliance_win case(id_nag) flags = & flag_nag_debug//& diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 4cfe571b6f..4e3be56475 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -542,6 +542,12 @@ end subroutine list_files logical function exists(filename) result(r) character(len=*), intent(in) :: filename inquire(file=filename, exist=r) + + !> Directories are not files for the Intel compilers. If so, also use this compiler-dependent extension +#if defined(__INTEL_COMPILER) + if (.not.r) inquire(directory=filename, exist=r) +#endif + end function diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 75fbb21d2b..0e01ac5768 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -56,8 +56,8 @@ subroutine get_global_settings(global_settings, error) ! Use custom path to the config file if it was specified. if (global_settings%has_custom_location()) then ! Throw error if folder doesn't exist. - if (.not. exists(global_settings%path_to_config_folder)) then - call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'."); return + if (.not. exists(config_path(global_settings))) then + call fatal_error(error, "Folder not found: '"//config_path(global_settings)//"'."); return end if ! Throw error if the file doesn't exist. @@ -115,7 +115,7 @@ subroutine use_default_registry_settings(global_settings) allocate (global_settings%registry_settings) global_settings%registry_settings%url = official_registry_base_url - global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & + global_settings%registry_settings%cache_path = join_path(config_path(global_settings), & & 'dependencies') end subroutine use_default_registry_settings @@ -155,7 +155,7 @@ subroutine get_registry_settings(table, global_settings, error) global_settings%registry_settings%path = path else ! Get canonical, absolute path on both Unix and Windows. - call get_absolute_path(join_path(global_settings%path_to_config_folder, path), & + call get_absolute_path(join_path(config_path(global_settings), path), & & global_settings%registry_settings%path, error) if (allocated(error)) return @@ -201,15 +201,15 @@ subroutine get_registry_settings(table, global_settings, error) if (.not. exists(cache_path)) call mkdir(cache_path) global_settings%registry_settings%cache_path = cache_path else - cache_path = join_path(global_settings%path_to_config_folder, cache_path) + cache_path = join_path(config_path(global_settings), cache_path) if (.not. exists(cache_path)) call mkdir(cache_path) ! Get canonical, absolute path on both Unix and Windows. call get_absolute_path(cache_path, global_settings%registry_settings%cache_path, error) if (allocated(error)) return end if else if (.not. allocated(path)) then - global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder, & - & 'dependencies') + global_settings%registry_settings%cache_path = join_path(config_path(global_settings), & + & 'dependencies') end if end subroutine get_registry_settings @@ -218,6 +218,8 @@ pure logical function has_custom_location(self) class(fpm_global_settings), intent(in) :: self has_custom_location = allocated(self%path_to_config_folder) .and. allocated(self%config_file_name) + if (.not.has_custom_location) return + has_custom_location = len_trim(self%path_to_config_folder)>0 .and. len_trim(self%config_file_name)>0 end function !> The full path to the global config file. @@ -225,7 +227,19 @@ function full_path(self) result(result) class(fpm_global_settings), intent(in) :: self character(len=:), allocatable :: result - result = join_path(self%path_to_config_folder, self%config_file_name) + result = join_path(config_path(self), self%config_file_name) end function + !> The path to the global config directory. + function config_path(self) + class(fpm_global_settings), intent(in) :: self + character(len=:), allocatable :: config_path + + if (allocated(self%path_to_config_folder)) then + config_path = self%path_to_config_folder + else + config_path = "" + end if + end function config_path + end module fpm_settings diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 6d22ef4a6c..88c3fc5c10 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -123,7 +123,7 @@ function parse_f_source(f_filename,error) result(f_source) ! Detect exported C-API via bind(C) if (.not.inside_interface .and. & parse_subsequence(file_lines_lower(i)%s,'bind','(','c')) then - + do j=i,1,-1 if (index(file_lines_lower(j)%s,'function') > 0 .or. & @@ -302,7 +302,7 @@ function parse_f_source(f_filename,error) result(f_source) f_source%unit_type = FPM_UNIT_MODULE end if - if (.not.inside_module) then + if (.not.inside_module) then inside_module = .true. else ! Must have missed an end module statement (can't assume a pure module) @@ -341,7 +341,7 @@ function parse_f_source(f_filename,error) result(f_source) file_lines_lower(i)%s) return end if - + if (f_source%unit_type /= FPM_UNIT_PROGRAM) then f_source%unit_type = FPM_UNIT_SUBMODULE end if @@ -403,7 +403,7 @@ function parse_f_source(f_filename,error) result(f_source) ! (to check for code outside of modules) if (parse_sequence(file_lines_lower(i)%s,'end','module') .or. & parse_sequence(file_lines_lower(i)%s,'end','submodule')) then - + inside_module = .false. cycle @@ -460,7 +460,7 @@ function parse_c_source(c_filename,error) result(c_source) c_source%unit_type = FPM_UNIT_CHEADER - else if (str_ends_with(lower(c_filename), ".cpp")) then + else if (str_ends_with(lower(c_filename), ".cpp")) then c_source%unit_type = FPM_UNIT_CPPSOURCE @@ -542,6 +542,7 @@ function split_n(string,delims,n,stat) result(substring) if (n<1) then i = size(string_parts) + n if (i < 1) then + allocate(character(len=0) :: substring) ! ifort bus error otherwise stat = 1 return end if @@ -550,6 +551,7 @@ function split_n(string,delims,n,stat) result(substring) end if if (i>size(string_parts)) then + allocate(character(len=0) :: substring) ! ifort bus error otherwise stat = 1 return end if @@ -573,7 +575,7 @@ function parse_subsequence(string,t1,t2,t3,t4) result(found) found = .false. offset = 1 - do + do i = index(string(offset:),t1) diff --git a/src/fpm_sources.f90 b/src/fpm_sources.f90 index 68251e59e5..0165249f50 100644 --- a/src/fpm_sources.f90 +++ b/src/fpm_sources.f90 @@ -7,6 +7,7 @@ module fpm_sources use fpm_error, only: error_t use fpm_model, only: srcfile_t, FPM_UNIT_PROGRAM use fpm_filesystem, only: basename, canon_path, dirname, join_path, list_files, is_hidden_file +use fpm_environment, only: get_os_type,OS_WINDOWS use fpm_strings, only: lower, str_ends_with, string_t, operator(.in.) use fpm_source_parsing, only: parse_f_source, parse_c_source use fpm_manifest_executable, only: executable_config_t @@ -14,6 +15,7 @@ module fpm_sources private public :: add_sources_from_dir, add_executable_sources +public :: get_exe_name_with_suffix character(4), parameter :: fortran_suffixes(2) = [".f90", & ".f "] @@ -232,4 +234,21 @@ subroutine get_executable_source_dirs(exe_dirs,executables) end subroutine get_executable_source_dirs +!> Build an executable name with suffix. Safe routine that always returns an allocated string +function get_exe_name_with_suffix(source) result(suffixed) + type(srcfile_t), intent(in) :: source + character(len=:), allocatable :: suffixed + + if (allocated(source%exe_name)) then + if (get_os_type() == OS_WINDOWS) then + suffixed = source%exe_name//'.exe' + else + suffixed = source%exe_name + end if + else + suffixed = "" + endif + +end function get_exe_name_with_suffix + end module fpm_sources diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index 9c2ccc07cd..2fa7c0df00 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -32,6 +32,7 @@ module fpm_targets use fpm_filesystem, only: dirname, join_path, canon_path use fpm_strings, only: string_t, operator(.in.), string_cat, fnv_1a, resize, lower, str_ends_with use fpm_compiler, only: get_macros +use fpm_sources, only: get_exe_name_with_suffix implicit none private @@ -194,7 +195,7 @@ subroutine build_target_list(targets,model) type(fpm_model_t), intent(inout), target :: model integer :: i, j, n_source, exe_type - character(:), allocatable :: xsuffix, exe_dir, compile_flags + character(:), allocatable :: exe_dir, compile_flags logical :: with_lib ! Check for empty build (e.g. header-only lib) @@ -206,11 +207,6 @@ subroutine build_target_list(targets,model) return end if - if (get_os_type() == OS_WINDOWS) then - xsuffix = '.exe' - else - xsuffix = '' - end if with_lib = any([((model%packages(j)%sources(i)%unit_scope == FPM_SCOPE_LIB, & i=1,size(model%packages(j)%sources)), & @@ -304,8 +300,7 @@ subroutine build_target_list(targets,model) call add_target(targets,package=model%packages(j)%name,type = FPM_TARGET_EXECUTABLE,& link_libraries = sources(i)%link_libraries, & - output_name = join_path(exe_dir, & - sources(i)%exe_name//xsuffix)) + output_name = join_path(exe_dir,get_exe_name_with_suffix(sources(i)))) associate(target => targets(size(targets))%ptr) @@ -876,7 +871,8 @@ subroutine resolve_target_linking(targets, model) call get_link_objects(target%link_objects,target,is_exe=.true.) - local_link_flags = model%link_flags + local_link_flags = "" + if (allocated(model%link_flags)) local_link_flags = model%link_flags target%link_flags = model%link_flags//" "//string_cat(target%link_objects," ") if (allocated(target%link_libraries)) then diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index 69fd433145..dfc94d4daa 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -68,15 +68,15 @@ program main 'CMD="test proj1 p2 project3 --profile release -- arg1 -x ""and a long one""", & &NAME="proj1","p2","project3",profile="release" ARGS="""arg1"" ""-x"" ""and a long one""", ', & -'CMD="build", NAME= profile="",ARGS="",', & -'CMD="build --profile release", NAME= profile="release",ARGS="",', & +'CMD="build", NAME=, profile="",ARGS="",', & +'CMD="build --profile release", NAME=, profile="release",ARGS="",', & -'CMD="clean", NAME= ARGS="",', & -'CMD="clean --skip", C_S=T, NAME= ARGS="",', & -'CMD="clean --all", C_A=T, NAME= ARGS="",', & -'CMD="publish --token abc --show-package-version", SHOW_V=T, NAME= token="abc",ARGS="",', & -'CMD="publish --token abc --show-form-data", SHOW_F_D=T, NAME= token="abc",ARGS="",', & -'CMD="publish --token abc", NAME= token="abc",ARGS="",', & +'CMD="clean", NAME=, ARGS="",', & +'CMD="clean --skip", C_S=T, NAME=, ARGS="",', & +'CMD="clean --all", C_A=T, NAME=, ARGS="",', & +'CMD="publish --token abc --show-package-version", SHOW_V=T, NAME=, token="abc",ARGS="",', & +'CMD="publish --token abc --show-form-data", SHOW_F_D=T, NAME=, token="abc",ARGS="",', & +'CMD="publish --token abc", NAME=, token="abc",ARGS="",', & ' ' ] character(len=256) :: readme(3) @@ -251,11 +251,11 @@ subroutine parse() type is (fpm_run_settings) act_profile=settings%profile act_name=settings%name - act_args=settings%args + if (allocated(settings%args)) act_args=settings%args type is (fpm_test_settings) act_profile=settings%profile act_name=settings%name - act_args=settings%args + if (allocated(settings%args)) act_args=settings%args type is (fpm_clean_settings) act_c_s=settings%clean_skip act_c_a=settings%clean_call diff --git a/test/fpm_test/test_manifest.f90 b/test/fpm_test/test_manifest.f90 index cd2605f4e3..566c61283d 100644 --- a/test/fpm_test/test_manifest.f90 +++ b/test/fpm_test/test_manifest.f90 @@ -483,7 +483,7 @@ subroutine test_profiles(error) type(package_config_t) :: package character(len=*), parameter :: manifest = 'fpm-profiles.toml' integer :: unit - character(:), allocatable :: profile_name, compiler, flags + character(:), allocatable :: profile_name, compiler logical :: profile_found type(profile_config_t) :: chosen_profile @@ -536,8 +536,9 @@ subroutine test_profiles(error) profile_name = 'debug' compiler = 'ifort' call find_profile(package%profiles, profile_name, compiler, 3, profile_found, chosen_profile) - if (.not.(chosen_profile%flags.eq.' /warn:all /check:all /error-limit:1 /Od /Z7 /assume:byterecl /traceback')) then - call test_failed(error, "Failed to load built-in profile"//flags) + if (.not.(chosen_profile%flags.eq.& + ' /warn:all /check:all /error-limit:1 /Od /Z7 /assume:byterecl /standard-semantics /traceback')) then + call test_failed(error, "Failed to load built-in profile "//profile_name) return end if @@ -1382,7 +1383,7 @@ subroutine test_macro_parsing(error) type(error_t), allocatable, intent(out) :: error type(package_config_t) :: package - character(:), allocatable :: temp_file + character(:), allocatable :: temp_file,pkg_ver integer :: unit integer(compiler_enum) :: id @@ -1401,7 +1402,9 @@ subroutine test_macro_parsing(error) if (allocated(error)) return - if (get_macros(id, package%preprocess(1)%macros, package%version%s()) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then + pkg_ver = package%version%s() + + if (get_macros(id, package%preprocess(1)%macros, pkg_ver) /= " -DFOO -DBAR=2 -DVERSION=0.1.0") then call test_failed(error, "Macros were not parsed correctly") end if @@ -1414,12 +1417,13 @@ subroutine test_macro_parsing_dependency(error) !> Error handling type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: macrosPackage, macrosDependency + character(len=:), allocatable :: macros_package, macros_dependency type(package_config_t) :: package, dependency character(:), allocatable :: toml_file_package character(:), allocatable :: toml_file_dependency + character(:), allocatable :: pkg_ver,dep_ver integer :: unit integer(compiler_enum) :: id @@ -1456,10 +1460,13 @@ subroutine test_macro_parsing_dependency(error) if (allocated(error)) return - macrosPackage = get_macros(id, package%preprocess(1)%macros, package%version%s()) - macrosDependency = get_macros(id, dependency%preprocess(1)%macros, dependency%version%s()) + pkg_ver = package%version%s() + dep_ver = dependency%version%s() + + macros_package = get_macros(id, package%preprocess(1)%macros, pkg_ver) + macros_dependency = get_macros(id, dependency%preprocess(1)%macros, dep_ver) - if (macrosPackage == macrosDependency) then + if (macros_package == macros_dependency) then call test_failed(error, "Macros of package and dependency should not be equal") end if diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index d573ac0b78..594aa937a5 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -91,6 +91,7 @@ subroutine tilde_correct_separator(error) end if call get_absolute_path('~'//separator, result, error) + if (allocated(error)) return call get_home(home, error) if (allocated(error)) return @@ -137,6 +138,7 @@ subroutine abs_path_root(error) if (os_is_unix()) then call get_absolute_path('/', result, error) + if (allocated(error)) return if (result /= '/') then call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'"); return @@ -146,6 +148,7 @@ subroutine abs_path_root(error) home_path = home_drive//'\' call get_absolute_path(home_path, result, error) + if (allocated(error)) return if (result /= home_path) then call test_failed(error, "Result '"//result//"' doesn't equal input value: '"//home_path//"'"); return diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 3c5b0ee021..75a1cb255c 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -8,7 +8,7 @@ module test_package_dependencies use fpm_dependency use fpm_manifest_dependency use fpm_toml - use fpm_settings, only: fpm_global_settings, get_registry_settings + use fpm_settings, only: fpm_global_settings, get_registry_settings, get_global_settings use fpm_downloader, only: downloader_t use fpm_versioning, only: version_t use jonquil, only: json_object, json_value, json_loads, cast_to_object @@ -245,7 +245,8 @@ subroutine test_add_dependencies(error) return end if - call deps%resolve(".", error) + ! Do not use polymorphic version due to Ifort issue + call resolve_dependencies(deps, ".", error) if (allocated(error)) return if (.not. deps%finished()) then @@ -1425,6 +1426,30 @@ subroutine resolve_dependency_once(self, dependency, global_settings, root, erro end subroutine resolve_dependency_once + !> Resolve all dependencies in the tree + subroutine resolve_dependencies(self, root, error) + !> Instance of the dependency tree + type(mock_dependency_tree_t), intent(inout) :: self + !> Current installation prefix + character(len=*), intent(in) :: root + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(fpm_global_settings) :: global_settings + integer :: ii + + call get_global_settings(global_settings, error) + if (allocated(error)) return + + do ii = 1, self%ndep + call resolve_dependency_once(self, self%dep(ii), global_settings, root, error) + if (allocated(error)) exit + end do + + if (allocated(error)) return + + end subroutine resolve_dependencies + subroutine delete_tmp_folder if (is_dir(tmp_folder)) call os_delete_dir(os_is_unix(), tmp_folder) end From 69d26bf55276433d11a7bc92fc16afd2d4f78f40 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 25 May 2023 10:17:37 +0200 Subject: [PATCH 550/799] fortran-lang `minpack` --- ci/meta_tests.sh | 5 +++ .../metapackage_minpack/app/main.f90 | 7 ++++ example_packages/metapackage_minpack/fpm.toml | 2 + .../src/metapackage_minpack.f90 | 14 +++++++ src/fpm/manifest/meta.f90 | 10 ++++- src/fpm_meta.f90 | 40 +++++++++++++++++-- 6 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 example_packages/metapackage_minpack/app/main.f90 create mode 100644 example_packages/metapackage_minpack/fpm.toml create mode 100644 example_packages/metapackage_minpack/src/metapackage_minpack.f90 diff --git a/ci/meta_tests.sh b/ci/meta_tests.sh index 54c70ce381..c2911d2737 100755 --- a/ci/meta_tests.sh +++ b/ci/meta_tests.sh @@ -27,6 +27,11 @@ pushd metapackage_stdlib "$fpm" run --verbose popd +pushd metapackage_minpack +"$fpm" build --verbose +"$fpm" run --verbose +popd + pushd metapackage_mpi "$fpm" build --verbose "$fpm" run --verbose diff --git a/example_packages/metapackage_minpack/app/main.f90 b/example_packages/metapackage_minpack/app/main.f90 new file mode 100644 index 0000000000..64008e4102 --- /dev/null +++ b/example_packages/metapackage_minpack/app/main.f90 @@ -0,0 +1,7 @@ +program main + use metapackage_minpack, only: simple_test + implicit none + logical :: success + call simple_test(success) + stop merge(0,1,success) +end program main diff --git a/example_packages/metapackage_minpack/fpm.toml b/example_packages/metapackage_minpack/fpm.toml new file mode 100644 index 0000000000..f178da2a48 --- /dev/null +++ b/example_packages/metapackage_minpack/fpm.toml @@ -0,0 +1,2 @@ +name = "metapackage_minpack" +dependencies.minpack="*" diff --git a/example_packages/metapackage_minpack/src/metapackage_minpack.f90 b/example_packages/metapackage_minpack/src/metapackage_minpack.f90 new file mode 100644 index 0000000000..d09d778409 --- /dev/null +++ b/example_packages/metapackage_minpack/src/metapackage_minpack.f90 @@ -0,0 +1,14 @@ +module metapackage_minpack + use minpack_module, only: wp + use iso_fortran_env, only: real64 + implicit none + private + + public :: simple_test +contains + subroutine simple_test(success) + logical, intent(out) :: success + ! Success! can read minpack module + success = wp == real64 + end subroutine simple_test +end module metapackage_minpack diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index 5cfb48c342..17261960eb 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -45,6 +45,9 @@ module fpm_manifest_metapackages !> Request stdlib support type(metapackage_request_t) :: stdlib + !> fortran-lang minpack + type(metapackage_request_t) :: minpack + end type metapackage_config_t @@ -158,12 +161,15 @@ subroutine new_meta_config(self, table, error) !> The toml table is not checked here because it already passed !> the "new_dependencies" check - call new_request(self%openmp, "openmp", table, error); + call new_request(self%openmp, "openmp", table, error) if (allocated(error)) return call new_request(self%stdlib, "stdlib", table, error) if (allocated(error)) return + call new_request(self%minpack, "minpack", table, error) + if (allocated(error)) return + call new_request(self%mpi, "mpi", table, error) if (allocated(error)) return @@ -178,7 +184,7 @@ logical function is_meta_package(key) select case (key) !> Supported metapackages - case ("openmp","stdlib","mpi") + case ("openmp","stdlib","mpi","minpack") is_meta_package = .true. case default diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 438ceee5f0..a763cb9444 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -19,7 +19,7 @@ module fpm_meta use fpm_model use fpm_command_line use fpm_manifest_dependency, only: dependency_config_t -use fpm_git, only : git_target_branch +use fpm_git, only : git_target_branch, git_target_tag use fpm_manifest, only: package_config_t use fpm_environment, only: get_env,os_is_unix use fpm_filesystem, only: run, get_temp_filename, getline, exists, canon_path, is_dir, get_dos_path @@ -153,9 +153,10 @@ subroutine init_from_name(this,name,compiler,error) !> Initialize metapackage by name select case(name) - case("openmp"); call init_openmp(this,compiler,error) - case("stdlib"); call init_stdlib(this,compiler,error) - case("mpi"); call init_mpi (this,compiler,error) + case("openmp"); call init_openmp (this,compiler,error) + case("stdlib"); call init_stdlib (this,compiler,error) + case("minpack"); call init_minpack(this,compiler,error) + case("mpi"); call init_mpi (this,compiler,error) case default call syntax_error(error, "Package "//name//" is not supported in [metapackages]") return @@ -216,6 +217,30 @@ subroutine init_openmp(this,compiler,error) end subroutine init_openmp +!> Initialize minpack metapackage for the current system +subroutine init_minpack(this,compiler,error) + class(metapackage_t), intent(inout) :: this + type(compiler_t), intent(in) :: compiler + type(error_t), allocatable, intent(out) :: error + + !> Cleanup + call destroy(this) + + !> minpack is queried as a dependency from the official repository + this%has_dependencies = .true. + + allocate(this%dependency(1)) + + !> 1) minpack. There are no true releases currently. Fetch HEAD + this%dependency(1)%name = "minpack" + this%dependency(1)%git = git_target_tag("https://github.com/fortran-lang/minpack", "v2.0.0-rc.1") + if (.not.allocated(this%dependency(1)%git)) then + call fatal_error(error,'cannot initialize git repo dependency for minpack metapackage') + return + end if + +end subroutine init_minpack + !> Initialize stdlib metapackage for the current system subroutine init_stdlib(this,compiler,error) class(metapackage_t), intent(inout) :: this @@ -408,6 +433,13 @@ subroutine resolve_metapackage_model(model,package,settings,error) if (allocated(error)) return endif + ! stdlib + if (package%meta%minpack%on) then + call add_metapackage_model(model,package,settings,"minpack",error) + if (allocated(error)) return + endif + + ! Stdlib is not 100% thread safe. print a warning to the user if (package%meta%stdlib%on .and. package%meta%openmp%on) then write(stdout,'(a)')' both openmp and stdlib requested: some functions may not be thread-safe!' From 6d6411c8e13ec76087781c05f66da8764752c886 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 25 May 2023 11:17:39 +0200 Subject: [PATCH 551/799] always search `.exe` runner versions --- src/fpm_meta.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index a763cb9444..388757ba8a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -837,7 +837,7 @@ subroutine get_mpi_runner(command,verbose,error) logical, intent(in) :: verbose type(error_t), allocatable, intent(out) :: error - character(*), parameter :: try(*) = ['mpiexec','mpirun '] + character(*), parameter :: try(*) = ['mpiexec ','mpirun ','mpiexec.exe','mpirun.exe '] integer :: itri logical :: success From e7d7ac88c956c69a912c6faf97b9f85d76fe039e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 27 May 2023 16:57:32 +0700 Subject: [PATCH 552/799] Clean up fpm help new and add --help and --version to fpm help publish --- src/fpm_command_line.f90 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index f7a0b1380d..9e1a8e50d1 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -1125,13 +1125,15 @@ subroutine set_help() help_new=[character(len=80) :: & 'NAME ', & ' new(1) - the fpm(1) subcommand to initialize a new project ', & + ' ', & 'SYNOPSIS ', & - ' fpm new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & - ' [--full|--bare][--backfill] ', & + ' fpm new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & + ' [--full|--bare][--backfill] ', & ' fpm new --help|--version ', & ' ', & 'DESCRIPTION ', & ' "fpm new" creates and populates a new programming project directory. ', & + ' ', & ' It ', & ' o creates a directory with the specified name ', & ' o runs the command "git init" in that directory ', & @@ -1361,6 +1363,8 @@ subroutine set_help() 'SYNOPSIS', & ' fpm publish [--token TOKEN]', & '', & + ' fpm publish --help|--version', & + '', & 'DESCRIPTION', & ' Collect relevant source files and upload package to the registry.', & ' It is mandatory to provide a token. The token can be generated on the', & @@ -1369,6 +1373,8 @@ subroutine set_help() 'OPTIONS', & ' --show-package-version show package version without publishing', & ' --show-form-data show sent form data without publishing', & + ' --help print this help and exit', & + ' --version print program version information and exit', & '' ] end subroutine set_help From 17fb88c9c0f692be06cba325358d7391bfc23fea Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 27 May 2023 17:12:03 +0700 Subject: [PATCH 553/799] Rename --show-form-data to --show-upload-data --- src/fpm/cmd/publish.f90 | 16 ++++++++-------- src/fpm_command_line.f90 | 12 ++++++------ test/cli_test/cli_test.f90 | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index dc83880f14..97b1e6d0d8 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -30,7 +30,7 @@ subroutine cmd_publish(settings) type(fpm_model_t) :: model type(error_t), allocatable :: error type(version_t), allocatable :: version - type(string_t), allocatable :: form_data(:) + type(string_t), allocatable :: upload_data(:) character(len=:), allocatable :: tmp_file type(downloader_t) :: downloader integer :: i @@ -61,22 +61,22 @@ subroutine cmd_publish(settings) end if end do - form_data = [ & + upload_data = [ & string_t('package_name="'//package%name//'"'), & string_t('package_license="'//package%license//'"'), & string_t('package_version="'//version%s()//'"') & & ] - if (allocated(settings%token)) form_data = [form_data, string_t('upload_token="'//settings%token//'"')] + if (allocated(settings%token)) upload_data = [upload_data, string_t('upload_token="'//settings%token//'"')] tmp_file = get_temp_filename() call git_archive('.', tmp_file, error) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Pack error: '//error%message) - form_data = [form_data, string_t('tarball=@"'//tmp_file//'"')] + upload_data = [upload_data, string_t('tarball=@"'//tmp_file//'"')] - if (settings%show_form_data) then - do i = 1, size(form_data) - print *, form_data(i)%s + if (settings%show_upload_data) then + do i = 1, size(upload_data) + print *, upload_data(i)%s end do return end if @@ -84,7 +84,7 @@ subroutine cmd_publish(settings) ! Make sure a token is provided for publishing. if (.not. allocated(settings%token)) call fpm_stop(1, 'No token provided.') - call downloader%upload_form(official_registry_base_url//'/packages', form_data, error) + call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) end end diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 9e1a8e50d1..9141e36741 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -120,7 +120,7 @@ module fpm_command_line type, extends(fpm_build_settings) :: fpm_publish_settings logical :: show_package_version = .false. - logical :: show_form_data = .false. + logical :: show_upload_data = .false. character(len=:), allocatable :: token end type @@ -620,7 +620,7 @@ subroutine get_command_line_settings(cmd_settings) case('publish') call set_args(common_args // compiler_args //'& & --show-package-version F & - & --show-form-data F & + & --show-upload-data F & & --token " " & & --list F & & --show-model F & @@ -637,7 +637,7 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_publish_settings :: cmd_settings) cmd_settings = fpm_publish_settings( & & show_package_version = lget('show-package-version'), & - & show_form_data = lget('show-form-data'), & + & show_upload_data = lget('show-upload-data'), & & profile=val_profile,& & prune=.not.lget('no-prune'), & & compiler=val_compiler, & @@ -754,7 +754,7 @@ subroutine set_help() ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & ' clean [--skip] [--all] ', & - ' publish [--show-package-version] [--show-form-data] [--token TOKEN] ', & + ' publish [--show-package-version] [--show-upload-data] [--token TOKEN] ', & ' '] help_usage=[character(len=80) :: & '' ] @@ -878,7 +878,7 @@ subroutine set_help() ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & ' clean [--skip] [--all] ', & - ' publish [--show-package-version] [--show-form-data] [--token TOKEN] ', & + ' publish [--show-package-version] [--show-upload-data] [--token TOKEN] ', & ' ', & 'SUBCOMMAND OPTIONS ', & ' -C, --directory PATH', & @@ -1372,7 +1372,7 @@ subroutine set_help() '', & 'OPTIONS', & ' --show-package-version show package version without publishing', & - ' --show-form-data show sent form data without publishing', & + ' --show-upload-data show uploaded data without publishing', & ' --help print this help and exit', & ' --version print program version information and exit', & '' ] diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index dfc94d4daa..ca24b12122 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -30,12 +30,12 @@ program main logical :: c_s,act_c_s ; namelist/act_cli/act_c_s logical :: c_a,act_c_a ; namelist/act_cli/act_c_a logical :: show_v,act_show_v ; namelist/act_cli/act_show_v -logical :: show_f_d,act_show_f_d; namelist/act_cli/act_show_f_d +logical :: show_u_d,act_show_u_d; namelist/act_cli/act_show_u_d character(len=:), allocatable :: token, act_token ; namelist/act_cli/act_token character(len=:), allocatable :: profile,act_profile ; namelist/act_cli/act_profile character(len=:), allocatable :: args,act_args ; namelist/act_cli/act_args -namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args,show_v,show_f_d,token +namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args,show_v,show_u_d,token integer :: lun logical,allocatable :: tally(:) logical,allocatable :: subtally(:) @@ -75,7 +75,7 @@ program main 'CMD="clean --skip", C_S=T, NAME=, ARGS="",', & 'CMD="clean --all", C_A=T, NAME=, ARGS="",', & 'CMD="publish --token abc --show-package-version", SHOW_V=T, NAME=, token="abc",ARGS="",', & -'CMD="publish --token abc --show-form-data", SHOW_F_D=T, NAME=, token="abc",ARGS="",', & +'CMD="publish --token abc --show-upload-data", SHOW_U_D=T, NAME=, token="abc",ARGS="",', & 'CMD="publish --token abc", NAME=, token="abc",ARGS="",', & ' ' ] character(len=256) :: readme(3) @@ -110,7 +110,7 @@ program main c_s=.false. ! --skip c_a=.false. ! --all show_v=.false. ! --show-package-version - show_f_d=.false. ! --show-form-data + show_u_d=.false. ! --show-upload-data token='' ! --token TOKEN args=repeat(' ',132) ! -- ARGS cmd=repeat(' ',132) ! the command line arguments to test @@ -132,7 +132,7 @@ program main act_c_s=.false. act_c_a=.false. act_show_v=.false. - act_show_f_d=.false. + act_show_u_d=.false. act_token='' act_args=repeat(' ',132) read(lun,nml=act_cli,iostat=ios,iomsg=message) @@ -148,7 +148,7 @@ program main call test_test('WITH_TESTED',act_w_t.eqv.w_t) call test_test('WITH_TEST',act_w_t.eqv.w_t) call test_test('SHOW-PACKAGE-VERSION',act_show_v.eqv.show_v) - call test_test('SHOW-FORM-DATA',act_show_f_d.eqv.show_f_d) + call test_test('SHOW-UPLOAD-DATA',act_show_u_d.eqv.show_u_d) call test_test('TOKEN',act_token==token) call test_test('ARGS',act_args==args) if(all(subtally))then @@ -237,7 +237,7 @@ subroutine parse() act_c_s=.false. act_c_a=.false. act_show_v=.false. -act_show_f_d=.false. +act_show_u_d=.false. act_token='' act_profile='' @@ -262,7 +262,7 @@ subroutine parse() type is (fpm_install_settings) type is (fpm_publish_settings) act_show_v=settings%show_package_version - act_show_f_d=settings%show_form_data + act_show_u_d=settings%show_upload_data act_token=settings%token end select From 4aed890ec51b0ce289aeb02665f586df43313988 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 27 May 2023 17:17:30 +0700 Subject: [PATCH 554/799] Clean up fpm help list --- src/fpm_command_line.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 9141e36741..d40e66d5ba 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -958,7 +958,7 @@ subroutine set_help() ' list(1) - list summary of fpm(1) subcommands ', & ' ', & 'SYNOPSIS ', & - ' fpm list [-list] ', & + ' fpm list ', & ' ', & ' fpm list --help|--version ', & ' ', & From 2f2e4717d61124d225a6153b83763b233c70527d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 28 May 2023 09:21:17 +0700 Subject: [PATCH 555/799] Include steps in help --- src/fpm/cmd/publish.f90 | 8 ++++---- src/fpm_command_line.f90 | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 97b1e6d0d8..8ff3a7d7da 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -44,16 +44,16 @@ subroutine cmd_publish(settings) print *, version%s(); return end if - ! Build model to obtain dependency tree. - call build_model(model, settings%fpm_build_settings, package, error) - if (allocated(error)) call fpm_stop(1, '*cmd_build* Model error: '//error%message) - !> Checks before uploading the package. if (.not. allocated(package%license)) call fpm_stop(1, 'No license specified in fpm.toml.') if (.not. allocated(version)) call fpm_stop(1, 'No version specified in fpm.toml.') if (version%s() == '0') call fpm_stop(1, 'Invalid version: "'//version%s()//'".') if (.not. exists('fpm.toml')) call fpm_stop(1, "Cannot find 'fpm.toml' file. Are you in the project root?") + ! Build model to obtain dependency tree. + call build_model(model, settings%fpm_build_settings, package, error) + if (allocated(error)) call fpm_stop(1, '*cmd_build* Model error: '//error%message) + ! Check if package contains git dependencies. Only publish packages without git dependencies. do i = 1, model%deps%ndep if (allocated(model%deps%dep(i)%git)) then diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index d40e66d5ba..15e93d685c 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -1361,20 +1361,38 @@ subroutine set_help() ' publish(1) - publish package to the registry', & '', & 'SYNOPSIS', & - ' fpm publish [--token TOKEN]', & + ' fpm publish [--token TOKEN] [--show-package-version] [--show-upload-data]', & '', & ' fpm publish --help|--version', & '', & 'DESCRIPTION', & - ' Collect relevant source files and upload package to the registry.', & - ' It is mandatory to provide a token. The token can be generated on the', & - ' registry website and will be linked to your username and namespace.', & + ' Follow the steps to create a tarball and upload the package to the registry:', & + '', & + ' 1. Register on the website (https://registry-frontend.vercel.app/).', & + ' 2. Create a namespace. Uploaded packages must be assigned to a unique', & + ' namespace to avoid conflicts among packages with similar names. A', & + ' namespace can accommodate multiple packages.', & + ' 3. Create a token for that namespace. A token is linked to your username', & + ' and is used to authenticate you during the upload process. Do not share', & + ' the token with others.', & + ' 4. Run fpm publish --token TOKEN to upload the package to the registry.', & + ' But be aware that the upload is permanent. An uploaded package cannot be', & + ' deleted.', & + '', & + ' See documentation (https://fpm.fortran-lang.org/en/spec/publish.html) for', & + ' more information regarding the package upload.', & '', & 'OPTIONS', & ' --show-package-version show package version without publishing', & ' --show-upload-data show uploaded data without publishing', & ' --help print this help and exit', & ' --version print program version information and exit', & + '', & + 'EXAMPLES', & + '', & + ' fpm publish --show-package-version # show package version without publishing', & + ' fpm publish --show-upload-data # show upload data without publishing', & + ' fpm publish --token TOKEN # upload package to the registry using TOKEN', & '' ] end subroutine set_help From c3823ce9c377aae6b9dd69729dfa30df4486bf4e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 28 May 2023 10:19:21 +0700 Subject: [PATCH 556/799] Check for non-empty token --- src/fpm/cmd/publish.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 8ff3a7d7da..80cb3d82e9 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -82,7 +82,11 @@ subroutine cmd_publish(settings) end if ! Make sure a token is provided for publishing. - if (.not. allocated(settings%token)) call fpm_stop(1, 'No token provided.') + if (allocated(settings%token)) then + if (settings%token == '') call fpm_stop(1, 'No token provided.') + else + call fpm_stop(1, 'No token provided.') + end if call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) From a7097ae005ecec348d7c219859e125ee4cddaae6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 28 May 2023 10:31:06 +0700 Subject: [PATCH 557/799] Improve error message for git dependencies --- src/fpm/cmd/publish.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 80cb3d82e9..e149c3075e 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -57,7 +57,8 @@ subroutine cmd_publish(settings) ! Check if package contains git dependencies. Only publish packages without git dependencies. do i = 1, model%deps%ndep if (allocated(model%deps%dep(i)%git)) then - call fpm_stop(1, "Do not publish packages containing git dependencies. '"//model%deps%dep(i)%name//"' is a git dependency.") + call fpm_stop(1, 'Do not publish packages containing git dependencies. '// & + & "Please upload '"//model%deps%dep(i)%name//"' to the registry first.") end if end do From 41166f8b95d879f047835e64ace5b3883e0018af Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 28 May 2023 12:03:52 +0700 Subject: [PATCH 558/799] Add link to documentation for package usage --- src/fpm_command_line.f90 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 15e93d685c..57e804035b 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -1366,7 +1366,7 @@ subroutine set_help() ' fpm publish --help|--version', & '', & 'DESCRIPTION', & - ' Follow the steps to create a tarball and upload the package to the registry:', & + ' Follow the steps to create a tarball and upload a package to the registry:', & '', & ' 1. Register on the website (https://registry-frontend.vercel.app/).', & ' 2. Create a namespace. Uploaded packages must be assigned to a unique', & @@ -1379,8 +1379,13 @@ subroutine set_help() ' But be aware that the upload is permanent. An uploaded package cannot be', & ' deleted.', & '', & - ' See documentation (https://fpm.fortran-lang.org/en/spec/publish.html) for', & - ' more information regarding the package upload.', & + ' See documentation for more information regarding the package upload and usage:', & + '', & + ' Package upload:', & + ' https://fpm.fortran-lang.org/en/spec/publish.html', & + '', & + ' Package usage:', & + ' https://fpm.fortran-lang.org/en/spec/manifest.html#dependencies-from-a-registry', & '', & 'OPTIONS', & ' --show-package-version show package version without publishing', & From c5ab931e173fe34c787d17e53b99e7f0ecc74af7 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 28 May 2023 12:47:30 +0700 Subject: [PATCH 559/799] Require module-naming --- src/fpm/cmd/publish.f90 | 2 ++ src/fpm_command_line.f90 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index e149c3075e..b84fba640a 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -46,6 +46,8 @@ subroutine cmd_publish(settings) !> Checks before uploading the package. if (.not. allocated(package%license)) call fpm_stop(1, 'No license specified in fpm.toml.') + if (.not. package%build%module_naming) call fpm_stop(1, 'The package does not meet the module naming requirements. '// & + & 'Please set "module_naming = true" in fpm.toml [build] or specify a custom module prefix.') if (.not. allocated(version)) call fpm_stop(1, 'No version specified in fpm.toml.') if (version%s() == '0') call fpm_stop(1, 'Invalid version: "'//version%s()//'".') if (.not. exists('fpm.toml')) call fpm_stop(1, "Cannot find 'fpm.toml' file. Are you in the project root?") diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 57e804035b..22ba9c0843 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -754,7 +754,7 @@ subroutine set_help() ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & ' clean [--skip] [--all] ', & - ' publish [--show-package-version] [--show-upload-data] [--token TOKEN] ', & + ' publish [--show-package-version] [--show-upload-data] [--token TOKEN] ', & ' '] help_usage=[character(len=80) :: & '' ] From 8eadf4ae2bbff9f08f591d467d873fe4de5c90d6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 29 May 2023 09:26:57 +0700 Subject: [PATCH 560/799] Clean up code and delete file after use --- src/fpm/cmd/publish.f90 | 29 ++++++++++++++++------------- src/fpm/git.f90 | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index b84fba640a..72714206b7 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -8,7 +8,7 @@ module fpm_cmd_publish use fpm_model, only: fpm_model_t use fpm_error, only: error_t, fpm_stop use fpm_versioning, only: version_t - use fpm_filesystem, only: exists, join_path, get_temp_filename + use fpm_filesystem, only: exists, join_path, get_temp_filename, delete_file use fpm_git, only: git_archive use fpm_downloader, only: downloader_t use fpm_strings, only: string_t @@ -64,34 +64,37 @@ subroutine cmd_publish(settings) end if end do + tmp_file = get_temp_filename() + call git_archive('.', tmp_file, error) + if (allocated(error)) call fpm_stop(1, '*cmd_publish* Archive error: '//error%message) + upload_data = [ & - string_t('package_name="'//package%name//'"'), & - string_t('package_license="'//package%license//'"'), & - string_t('package_version="'//version%s()//'"') & - & ] + & string_t('package_name="'//package%name//'"'), & + & string_t('package_license="'//package%license//'"'), & + & string_t('package_version="'//version%s()//'"'), & + & string_t('tarball=@"'//tmp_file//'"') & + & ] if (allocated(settings%token)) upload_data = [upload_data, string_t('upload_token="'//settings%token//'"')] - tmp_file = get_temp_filename() - call git_archive('.', tmp_file, error) - if (allocated(error)) call fpm_stop(1, '*cmd_publish* Pack error: '//error%message) - upload_data = [upload_data, string_t('tarball=@"'//tmp_file//'"')] - if (settings%show_upload_data) then do i = 1, size(upload_data) print *, upload_data(i)%s end do - return + call delete_file(tmp_file); return end if ! Make sure a token is provided for publishing. if (allocated(settings%token)) then - if (settings%token == '') call fpm_stop(1, 'No token provided.') + if (settings%token == '') then + call delete_file(tmp_file); call fpm_stop(1, 'No token provided.') + end if else - call fpm_stop(1, 'No token provided.') + call delete_file(tmp_file); call fpm_stop(1, 'No token provided.') end if call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) + call delete_file(tmp_file) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) end end diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index b1cd1d8376..ad86ca3f73 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -328,7 +328,7 @@ subroutine git_archive(source, destination, error) call fatal_error(error, "Cannot find a suitable archive format for 'git archive'."); return end if - call execute_command_line('git archive HEAD --format='//archive_format//' -o '// destination, exitstat=stat) + call execute_command_line('git archive HEAD --format='//archive_format//' -o '//destination, exitstat=stat) if (stat /= 0) then call fatal_error(error, "Error packing '"//source//"'."); return end if From 9c64d18bfba07779a4fa83cb83e072d16142e4ca Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 29 May 2023 10:43:33 +0700 Subject: [PATCH 561/799] Add dry run option, add tests --- src/fpm/cmd/publish.f90 | 9 ++++++++- src/fpm_command_line.f90 | 12 ++++++++++-- test/cli_test/cli_test.f90 | 9 ++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 72714206b7..0e0c85c663 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -81,7 +81,6 @@ subroutine cmd_publish(settings) do i = 1, size(upload_data) print *, upload_data(i)%s end do - call delete_file(tmp_file); return end if ! Make sure a token is provided for publishing. @@ -93,6 +92,14 @@ subroutine cmd_publish(settings) call delete_file(tmp_file); call fpm_stop(1, 'No token provided.') end if + ! Perform network request and validate package on the backend as soon as + ! https://github.com/fortran-lang/registry/issues/41 is resolved. + if (settings%is_dry_run) then + print *, 'Dry run successful.' + print *, '' + print *, 'tarball generated for upload: ', tmp_file; return + end if + call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) call delete_file(tmp_file) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 22ba9c0843..2537f701bd 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -121,6 +121,7 @@ module fpm_command_line type, extends(fpm_build_settings) :: fpm_publish_settings logical :: show_package_version = .false. logical :: show_upload_data = .false. + logical :: is_dry_run = .false. character(len=:), allocatable :: token end type @@ -621,6 +622,7 @@ subroutine get_command_line_settings(cmd_settings) call set_args(common_args // compiler_args //'& & --show-package-version F & & --show-upload-data F & + & --dry-run F & & --token " " & & --list F & & --show-model F & @@ -638,6 +640,7 @@ subroutine get_command_line_settings(cmd_settings) cmd_settings = fpm_publish_settings( & & show_package_version = lget('show-package-version'), & & show_upload_data = lget('show-upload-data'), & + & is_dry_run = lget('dry-run'), & & profile=val_profile,& & prune=.not.lget('no-prune'), & & compiler=val_compiler, & @@ -754,7 +757,8 @@ subroutine set_help() ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & ' clean [--skip] [--all] ', & - ' publish [--show-package-version] [--show-upload-data] [--token TOKEN] ', & + ' publish [--token TOKEN] [--show-package-version] [--show-upload-data] ', & + ' [--dry-run] ', & ' '] help_usage=[character(len=80) :: & '' ] @@ -878,7 +882,8 @@ subroutine set_help() ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & ' clean [--skip] [--all] ', & - ' publish [--show-package-version] [--show-upload-data] [--token TOKEN] ', & + ' publish [--token TOKEN] [--show-package-version] [--show-upload-data] ', & + ' [--dry-run] ', & ' ', & 'SUBCOMMAND OPTIONS ', & ' -C, --directory PATH', & @@ -1362,6 +1367,7 @@ subroutine set_help() '', & 'SYNOPSIS', & ' fpm publish [--token TOKEN] [--show-package-version] [--show-upload-data]', & + ' [--dry-run] ', & '', & ' fpm publish --help|--version', & '', & @@ -1390,6 +1396,7 @@ subroutine set_help() 'OPTIONS', & ' --show-package-version show package version without publishing', & ' --show-upload-data show uploaded data without publishing', & + ' --dry-run create tarball for revision without publishing', & ' --help print this help and exit', & ' --version print program version information and exit', & '', & @@ -1397,6 +1404,7 @@ subroutine set_help() '', & ' fpm publish --show-package-version # show package version without publishing', & ' fpm publish --show-upload-data # show upload data without publishing', & + ' fpm publish --dry-run # create tarball without publishing', & ' fpm publish --token TOKEN # upload package to the registry using TOKEN', & '' ] end subroutine set_help diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index ca24b12122..f5336b62ca 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -31,11 +31,12 @@ program main logical :: c_a,act_c_a ; namelist/act_cli/act_c_a logical :: show_v,act_show_v ; namelist/act_cli/act_show_v logical :: show_u_d,act_show_u_d; namelist/act_cli/act_show_u_d +logical :: dry_run,act_dry_run ; namelist/act_cli/act_dry_run character(len=:), allocatable :: token, act_token ; namelist/act_cli/act_token character(len=:), allocatable :: profile,act_profile ; namelist/act_cli/act_profile character(len=:), allocatable :: args,act_args ; namelist/act_cli/act_args -namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args,show_v,show_u_d,token +namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args,show_v,show_u_d,dry_run,token integer :: lun logical,allocatable :: tally(:) logical,allocatable :: subtally(:) @@ -76,6 +77,7 @@ program main 'CMD="clean --all", C_A=T, NAME=, ARGS="",', & 'CMD="publish --token abc --show-package-version", SHOW_V=T, NAME=, token="abc",ARGS="",', & 'CMD="publish --token abc --show-upload-data", SHOW_U_D=T, NAME=, token="abc",ARGS="",', & +'CMD="publish --token abc --dry-run", DRY_RUN=T, NAME=, token="abc",ARGS="",', & 'CMD="publish --token abc", NAME=, token="abc",ARGS="",', & ' ' ] character(len=256) :: readme(3) @@ -111,6 +113,7 @@ program main c_a=.false. ! --all show_v=.false. ! --show-package-version show_u_d=.false. ! --show-upload-data + dry_run=.false. ! --dry-run token='' ! --token TOKEN args=repeat(' ',132) ! -- ARGS cmd=repeat(' ',132) ! the command line arguments to test @@ -133,6 +136,7 @@ program main act_c_a=.false. act_show_v=.false. act_show_u_d=.false. + act_dry_run=.false. act_token='' act_args=repeat(' ',132) read(lun,nml=act_cli,iostat=ios,iomsg=message) @@ -149,6 +153,7 @@ program main call test_test('WITH_TEST',act_w_t.eqv.w_t) call test_test('SHOW-PACKAGE-VERSION',act_show_v.eqv.show_v) call test_test('SHOW-UPLOAD-DATA',act_show_u_d.eqv.show_u_d) + call test_test('DRY-RUN',act_dry_run.eqv.dry_run) call test_test('TOKEN',act_token==token) call test_test('ARGS',act_args==args) if(all(subtally))then @@ -238,6 +243,7 @@ subroutine parse() act_c_a=.false. act_show_v=.false. act_show_u_d=.false. +act_dry_run=.false. act_token='' act_profile='' @@ -263,6 +269,7 @@ subroutine parse() type is (fpm_publish_settings) act_show_v=settings%show_package_version act_show_u_d=settings%show_upload_data + act_dry_run=settings%is_dry_run act_token=settings%token end select From 01152069082859c8218e4ec78f3f148a607afa38 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 29 May 2023 19:11:49 +0700 Subject: [PATCH 562/799] Add back return --- src/fpm/cmd/publish.f90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 0e0c85c663..a81e2e2c92 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -81,6 +81,7 @@ subroutine cmd_publish(settings) do i = 1, size(upload_data) print *, upload_data(i)%s end do + return end if ! Make sure a token is provided for publishing. @@ -97,7 +98,8 @@ subroutine cmd_publish(settings) if (settings%is_dry_run) then print *, 'Dry run successful.' print *, '' - print *, 'tarball generated for upload: ', tmp_file; return + print *, 'tarball generated for upload: ', tmp_file + return end if call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) From 0c860f28d106f659243b031b8364752c44020ac6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 30 May 2023 15:40:38 +0700 Subject: [PATCH 563/799] Remove these these --- src/fpm_command_line.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 2537f701bd..94a385955d 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -169,7 +169,7 @@ module fpm_command_line ' --flag FFLAGS selects compile arguments for the build, the default value is',& ' set by the FPM_FFLAGS environment variable. These are added ',& ' to the profile options if --profile is specified, else these ',& - ' these options override the defaults. Note object and .mod ',& + ' options override the defaults. Note object and .mod ',& ' directory locations are always built in. ',& ' --c-flag CFLAGS selects compile arguments specific for C source in the build.',& ' The default value is set by the FPM_CFLAGS environment ',& From 91c425ec40b59b73d3944db1c3fef1b688b30ec4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 30 May 2023 19:00:12 +0700 Subject: [PATCH 564/799] Add verbose mode and --token TOKEN to --dry-run example --- src/fpm/cmd/publish.f90 | 23 ++++++++++++++++++----- src/fpm_command_line.f90 | 21 +++++++++++---------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index a81e2e2c92..a1b2981372 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -78,10 +78,7 @@ subroutine cmd_publish(settings) if (allocated(settings%token)) upload_data = [upload_data, string_t('upload_token="'//settings%token//'"')] if (settings%show_upload_data) then - do i = 1, size(upload_data) - print *, upload_data(i)%s - end do - return + call print_upload_data(upload_data); return end if ! Make sure a token is provided for publishing. @@ -93,7 +90,13 @@ subroutine cmd_publish(settings) call delete_file(tmp_file); call fpm_stop(1, 'No token provided.') end if - ! Perform network request and validate package on the backend as soon as + if (settings%verbose) then + print *, '' + call print_upload_data(upload_data) + print *, '' + end if + + ! Perform network request and validate package, token etc. on the backend once ! https://github.com/fortran-lang/registry/issues/41 is resolved. if (settings%is_dry_run) then print *, 'Dry run successful.' @@ -106,4 +109,14 @@ subroutine cmd_publish(settings) call delete_file(tmp_file) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) end + + subroutine print_upload_data(upload_data) + type(string_t), intent(in) :: upload_data(:) + integer :: i + + print *, 'Upload data:' + do i = 1, size(upload_data) + print *, upload_data(i)%s + end do + end end diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 94a385955d..f1ced79308 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -758,7 +758,7 @@ subroutine set_help() ' [options] ', & ' clean [--skip] [--all] ', & ' publish [--token TOKEN] [--show-package-version] [--show-upload-data] ', & - ' [--dry-run] ', & + ' [--dry-run] [--verbose] ', & ' '] help_usage=[character(len=80) :: & '' ] @@ -883,7 +883,7 @@ subroutine set_help() ' [options] ', & ' clean [--skip] [--all] ', & ' publish [--token TOKEN] [--show-package-version] [--show-upload-data] ', & - ' [--dry-run] ', & + ' [--dry-run] [--verbose] ', & ' ', & 'SUBCOMMAND OPTIONS ', & ' -C, --directory PATH', & @@ -1367,7 +1367,7 @@ subroutine set_help() '', & 'SYNOPSIS', & ' fpm publish [--token TOKEN] [--show-package-version] [--show-upload-data]', & - ' [--dry-run] ', & + ' [--dry-run] [--verbose] ', & '', & ' fpm publish --help|--version', & '', & @@ -1385,7 +1385,7 @@ subroutine set_help() ' But be aware that the upload is permanent. An uploaded package cannot be', & ' deleted.', & '', & - ' See documentation for more information regarding the package upload and usage:', & + ' See documentation for more information regarding package upload and usage:', & '', & ' Package upload:', & ' https://fpm.fortran-lang.org/en/spec/publish.html', & @@ -1395,17 +1395,18 @@ subroutine set_help() '', & 'OPTIONS', & ' --show-package-version show package version without publishing', & - ' --show-upload-data show uploaded data without publishing', & - ' --dry-run create tarball for revision without publishing', & + ' --show-upload-data show upload data without publishing', & + ' --dry-run perform dry run without publishing', & ' --help print this help and exit', & ' --version print program version information and exit', & + ' --verbose print more information', & '', & 'EXAMPLES', & '', & - ' fpm publish --show-package-version # show package version without publishing', & - ' fpm publish --show-upload-data # show upload data without publishing', & - ' fpm publish --dry-run # create tarball without publishing', & - ' fpm publish --token TOKEN # upload package to the registry using TOKEN', & + ' fpm publish --show-package-version # show package version without publishing', & + ' fpm publish --show-upload-data # show upload data without publishing', & + ' fpm publish --token TOKEN --dry-run # perform dry run without publishing', & + ' fpm publish --token TOKEN # upload package to the registry', & '' ] end subroutine set_help From 4c0d5a6f6667bc8e4863acd7afec2d6c87c32f60 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 30 May 2023 19:19:55 +0700 Subject: [PATCH 565/799] Do not imply manual upload --- src/fpm/cmd/publish.f90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index a1b2981372..5439f82ba5 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -99,10 +99,7 @@ subroutine cmd_publish(settings) ! Perform network request and validate package, token etc. on the backend once ! https://github.com/fortran-lang/registry/issues/41 is resolved. if (settings%is_dry_run) then - print *, 'Dry run successful.' - print *, '' - print *, 'tarball generated for upload: ', tmp_file - return + print *, 'Dry run successful. ', 'Generated tarball: ', tmp_file; return end if call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) From cc7cedb5f6d7acf572c592f497d40d9e2cf1808f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Tue, 30 May 2023 19:21:10 +0700 Subject: [PATCH 566/799] Only use one string --- src/fpm/cmd/publish.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 5439f82ba5..22c283dac8 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -99,7 +99,7 @@ subroutine cmd_publish(settings) ! Perform network request and validate package, token etc. on the backend once ! https://github.com/fortran-lang/registry/issues/41 is resolved. if (settings%is_dry_run) then - print *, 'Dry run successful. ', 'Generated tarball: ', tmp_file; return + print *, 'Dry run successful. Generated tarball: ', tmp_file; return end if call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) From 1bae477620cc482a0787fa7617266baa93612265 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 05:51:07 -0500 Subject: [PATCH 567/799] parse `non_intrinsic` and `intrinsic` `use`d modules --- src/fpm_source_parsing.f90 | 161 ++++++++++++++++++++++++------------- 1 file changed, 107 insertions(+), 54 deletions(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 88c3fc5c10..58d16c0afb 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -27,15 +27,7 @@ module fpm_source_parsing implicit none private -public :: parse_f_source, parse_c_source - -character(15), parameter :: INTRINSIC_MODULE_NAMES(*) = & - ['iso_c_binding ', & - 'iso_fortran_env', & - 'ieee_arithmetic', & - 'ieee_exceptions', & - 'ieee_features ', & - 'omp_lib '] +public :: parse_f_source, parse_c_source, parse_use_statement contains @@ -77,7 +69,7 @@ function parse_f_source(f_filename,error) result(f_source) type(srcfile_t) :: f_source type(error_t), allocatable, intent(out) :: error - logical :: inside_module, inside_interface + logical :: inside_module, inside_interface, using, intrinsic_module integer :: stat integer :: fh, n_use, n_include, n_mod, n_parent, i, j, ic, pass type(string_t), allocatable :: file_lines(:), file_lines_lower(:) @@ -179,59 +171,24 @@ function parse_f_source(f_filename,error) result(f_source) end if ! Process 'USE' statements - if (index(file_lines_lower(i)%s,'use ') == 1 .or. & - index(file_lines_lower(i)%s,'use::') == 1) then - - if (index(file_lines_lower(i)%s,'::') > 0) then - - temp_string = split_n(file_lines_lower(i)%s,delims=':',n=2,stat=stat) - if (stat /= 0) then - call file_parse_error(error,f_filename, & - 'unable to find used module name',i, & - file_lines_lower(i)%s,index(file_lines_lower(i)%s,'::')) - return - end if + call parse_use_statement(f_filename,i,file_lines_lower(i)%s,using,intrinsic_module,mod_name,error) + if (allocated(error)) return - mod_name = split_n(temp_string,delims=' ,',n=1,stat=stat) - if (stat /= 0) then - call file_parse_error(error,f_filename, & - 'unable to find used module name',i, & - file_lines_lower(i)%s) - return - end if + if (using) then - else + ! Not a valid module name? + if (.not.is_fortran_name(mod_name)) cycle - mod_name = split_n(file_lines_lower(i)%s,n=2,delims=' ,',stat=stat) - if (stat /= 0) then - call file_parse_error(error,f_filename, & - 'unable to find used module name',i, & - file_lines_lower(i)%s) - return - end if - - end if - - if (.not.is_fortran_name(mod_name)) then - cycle - end if - - if (any([(index(mod_name,trim(INTRINSIC_MODULE_NAMES(j)))>0, & - j=1,size(INTRINSIC_MODULE_NAMES))])) then - cycle - end if + ! Valid intrinsic module: not a dependency + if (intrinsic_module) cycle n_use = n_use + 1 - if (pass == 2) then - - f_source%modules_used(n_use)%s = mod_name - - end if + if (pass == 2) f_source%modules_used(n_use)%s = mod_name cycle - end if + endif ! Process 'INCLUDE' statements ic = index(file_lines_lower(i)%s,'include') @@ -655,5 +612,101 @@ function parse_sequence(string,t1,t2,t3,t4) result(found) end function parse_sequence +! Process 'USE' statements + +! USE [, intrinsic] :: module_name [, only: only_list] +! USE [, non_intrinsic] :: module_name [, only: only_list] +subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_name,error) + character(*), intent(in) :: f_filename,line + integer, intent(in) :: i ! line number + logical, intent(out) :: use_stmt,is_intrinsic + character(:), allocatable, intent(out) :: module_name + type(error_t), allocatable, intent(out) :: error + + character(15), parameter :: INTRINSIC_NAMES(*) = & + ['iso_c_binding ', & + 'iso_fortran_env', & + 'ieee_arithmetic', & + 'ieee_exceptions', & + 'ieee_features ', & + 'omp_lib '] + + character(len=:), allocatable :: lowercase,temp_string + integer :: colons,intr,nonintr,j,stat + logical :: has_intrinsic_name + + use_stmt = .false. + is_intrinsic = .false. + if (len_trim(line)<=0) return + + ! Preprocess: lowercase, remove heading spaces + lowercase = lower(trim(adjustl(line))) + + ! 'use' should be the first string in the adjustl line + use_stmt = index(lowercase,'use')==1; if (.not.use_stmt) return + colons = index(lowercase,'::') + nonintr = 0 + intr = 0 + intrinsicness: if (colons>3) then + + end if intrinsicness + + ! If declared intrinsic, check that it is true + print *, 'colons=',colons + print *, 'intr=',intr + print *, 'nonintr=',nonintr + + if (colons>3) then + + ! If there is an intrinsic/non-intrinsic spec + nonintr = index(lowercase(1:colons-1),'non_intrinsic') + if (nonintr==0) intr = index(lowercase(1:colons-1),'intrinsic') + + + temp_string = split_n(lowercase,delims=':',n=2,stat=stat) + if (stat /= 0) then + call file_parse_error(error,f_filename, & + 'unable to find used module name',i, & + lowercase,colons) + return + end if + + module_name = split_n(temp_string,delims=' ,',n=1,stat=stat) + if (stat /= 0) then + call file_parse_error(error,f_filename, & + 'unable to find used module name',i, & + lowercase) + return + end if + + else + + module_name = split_n(lowercase,n=2,delims=' ,',stat=stat) + if (stat /= 0) then + call file_parse_error(error,f_filename, & + 'unable to find used module name',i, & + lowercase) + return + end if + + end if + + ! If declared intrinsic, check that it is true + has_intrinsic_name = any([(index(module_name,trim(INTRINSIC_NAMES(j)))>0, & + j=1,size(INTRINSIC_NAMES))]) + if (intr>0 .and. .not.has_intrinsic_name) then + call file_parse_error(error,f_filename, & + 'module is declared intrinsic but it is not ',i, & + lowercase) + return + endif + + ! Should we treat this as an intrinsic module + is_intrinsic = nonintr==0 .and. & ! not declared non-intrinsic + (intr>0 .or. has_intrinsic_name) + +end subroutine parse_use_statement + + end module fpm_source_parsing From 0ae912b2700c74e69f2cd774897ae89f527a558d Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 05:51:20 -0500 Subject: [PATCH 568/799] tests for `non_intrinsic` parsing --- test/fpm_test/test_source_parsing.f90 | 141 +++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 4 deletions(-) diff --git a/test/fpm_test/test_source_parsing.f90 b/test/fpm_test/test_source_parsing.f90 index b480e76c33..41d7db3a84 100644 --- a/test/fpm_test/test_source_parsing.f90 +++ b/test/fpm_test/test_source_parsing.f90 @@ -2,11 +2,12 @@ module test_source_parsing use testsuite, only : new_unittest, unittest_t, error_t, test_failed use fpm_filesystem, only: get_temp_filename - use fpm_source_parsing, only: parse_f_source, parse_c_source + use fpm_source_parsing, only: parse_f_source, parse_c_source, parse_use_statement use fpm_model, only: srcfile_t, FPM_UNIT_PROGRAM, FPM_UNIT_MODULE, & FPM_UNIT_SUBMODULE, FPM_UNIT_SUBPROGRAM, FPM_UNIT_CSOURCE, & FPM_UNIT_CPPSOURCE - use fpm_strings, only: operator(.in.) + use fpm_strings, only: operator(.in.), lower + use fpm_error, only: file_parse_error, fatal_error implicit none private @@ -14,7 +15,6 @@ module test_source_parsing contains - !> Collect all exported unit tests subroutine collect_source_parsing(testsuite) @@ -24,6 +24,7 @@ subroutine collect_source_parsing(testsuite) testsuite = [ & & new_unittest("modules-used", test_modules_used), & & new_unittest("intrinsic-modules-used", test_intrinsic_modules_used), & + & new_unittest("nonintrinsic-modules-used", test_nonintrinsic_modules_used), & & new_unittest("include-stmt", test_include_stmt), & & new_unittest("program", test_program), & & new_unittest("module", test_module), & @@ -42,7 +43,8 @@ subroutine collect_source_parsing(testsuite) & new_unittest("invalid-module", & test_invalid_module, should_fail=.true.), & & new_unittest("invalid-submodule", & - test_invalid_submodule, should_fail=.true.) & + test_invalid_submodule, should_fail=.true.), & + & new_unittest("use-statement",test_use_statement) & ] end subroutine collect_source_parsing @@ -187,6 +189,78 @@ subroutine test_intrinsic_modules_used(error) end subroutine test_intrinsic_modules_used + !> Check that intrinsic module names are not ignored if declared non_intrinsic + subroutine test_nonintrinsic_modules_used(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: unit + character(:), allocatable :: temp_file + type(srcfile_t), allocatable :: f_source + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'program test', & + & ' use, non_intrinsic :: iso_c_binding', & + & ' use, intrinsic :: iso_fortran_env', & + & ' use, non_intrinsic :: ieee_arithmetic', & + & ' use, non_intrinsic :: ieee_exceptions', & + & ' use, non_intrinsic :: ieee_features', & + & ' use, non_intrinsic :: my_module', & + & ' implicit none', & + & 'end program test' + close(unit) + + f_source = parse_f_source(temp_file,error) + if (allocated(error)) then + return + end if + + if (size(f_source%modules_provided) /= 0) then + call test_failed(error,'Unexpected modules_provided - expecting zero') + return + end if + + if (size(f_source%modules_used) /= 5) then + call test_failed(error,'Incorrect number of modules_used - expecting five') + return + end if + + if (.not. ('iso_c_binding' .in. f_source%modules_used)) then + call test_failed(error,'Non-Intrinsic module found in modules_used') + return + end if + + if ('iso_fortran_env' .in. f_source%modules_used) then + call test_failed(error,'Intrinsic module found in modules_used') + return + end if + + if (.not. ('ieee_arithmetic' .in. f_source%modules_used)) then + call test_failed(error,'Non-Intrinsic module found in modules_used') + return + end if + + if (.not. ('ieee_exceptions' .in. f_source%modules_used)) then + call test_failed(error,'Non-Intrinsic module found in modules_used') + return + end if + + if (.not. ('ieee_features' .in. f_source%modules_used)) then + call test_failed(error,'Non-Intrinsic module found in modules_used') + return + end if + + if (.not. ('my_module' .in. f_source%modules_used)) then + call test_failed(error,'Non-Intrinsic module found in modules_used') + return + end if + + end subroutine test_nonintrinsic_modules_used + !> Check parsing of include statements subroutine test_include_stmt(error) @@ -945,6 +1019,65 @@ subroutine test_invalid_submodule(error) end subroutine test_invalid_submodule + !> Parse several USE statements + subroutine test_use_statement(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + character(*), parameter :: filename='test_use_statement' + character(:), allocatable :: line,module_name + + logical :: used,is_intrinsic + + line = 'use, intrinsic:: iso_fortran_env' + call parse_use_statement(filename,0,line,used,is_intrinsic,module_name,error) + if (allocated(error)) return + + if (.not. (used .and. & + is_intrinsic .and. & + module_name=='iso_fortran_env' .and. & + used)) then + call fatal_error(error,'USE statement failed parsing <'//line//'>') + return + endif + + line = 'use, non_intrinsic :: iso_fortran_env' + call parse_use_statement(filename,0,line,used,is_intrinsic,module_name,error) + if (allocated(error)) return + + if (.not. (used .and. & + (.not.is_intrinsic) .and. & + module_name=='iso_fortran_env' .and. & + used)) then + call fatal_error(error,'USE statement failed parsing <'//line//'>') + return + endif + + line = 'use, non_intrinsic :: my_fortran_module' + call parse_use_statement(filename,0,line,used,is_intrinsic,module_name,error) + if (allocated(error)) return + + if (.not. (used .and. & + (.not.is_intrinsic) .and. & + module_name=='my_fortran_module' .and. & + used)) then + call fatal_error(error,'USE statement failed parsing <'//line//'>') + return + endif + + line = 'use, intrinsic :: my_fortran_module' + call parse_use_statement(filename,0,line,used,is_intrinsic,module_name,error) + + ! This is not an intrinsic module: should detect an error + if (.not. allocated(error)) then + call fatal_error(error,'Did not catch invalid intrinsic module in <'//line//'>') + return + else + deallocate(error) + endif + + end subroutine test_use_statement end module test_source_parsing From 3780d2b09b02b62a3f4a4e4888ba1f8453c13c23 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 05:53:10 -0500 Subject: [PATCH 569/799] cleanup --- src/fpm_source_parsing.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 58d16c0afb..12ae899402 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -652,10 +652,6 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na end if intrinsicness ! If declared intrinsic, check that it is true - print *, 'colons=',colons - print *, 'intr=',intr - print *, 'nonintr=',nonintr - if (colons>3) then ! If there is an intrinsic/non-intrinsic spec From 89f2004d0c24fd1fed45023d69e227064bcb8917 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 05:53:31 -0500 Subject: [PATCH 570/799] add example package --- example_packages/nonintrinsic/.gitignore | 1 + example_packages/nonintrinsic/app/main.f90 | 6 ++++++ example_packages/nonintrinsic/fpm.toml | 1 + example_packages/nonintrinsic/src/iso_fortran_env.f90 | 4 ++++ 4 files changed, 12 insertions(+) create mode 100644 example_packages/nonintrinsic/.gitignore create mode 100644 example_packages/nonintrinsic/app/main.f90 create mode 100644 example_packages/nonintrinsic/fpm.toml create mode 100644 example_packages/nonintrinsic/src/iso_fortran_env.f90 diff --git a/example_packages/nonintrinsic/.gitignore b/example_packages/nonintrinsic/.gitignore new file mode 100644 index 0000000000..a007feab07 --- /dev/null +++ b/example_packages/nonintrinsic/.gitignore @@ -0,0 +1 @@ +build/* diff --git a/example_packages/nonintrinsic/app/main.f90 b/example_packages/nonintrinsic/app/main.f90 new file mode 100644 index 0000000000..a45f06d8f7 --- /dev/null +++ b/example_packages/nonintrinsic/app/main.f90 @@ -0,0 +1,6 @@ +program test_nonintr + use, non_intrinsic :: iso_fortran_env + + ! ijk=0 can be read + stop ijk +end program test_nonintr diff --git a/example_packages/nonintrinsic/fpm.toml b/example_packages/nonintrinsic/fpm.toml new file mode 100644 index 0000000000..77e149814d --- /dev/null +++ b/example_packages/nonintrinsic/fpm.toml @@ -0,0 +1 @@ +name = "non-intrinsic" diff --git a/example_packages/nonintrinsic/src/iso_fortran_env.f90 b/example_packages/nonintrinsic/src/iso_fortran_env.f90 new file mode 100644 index 0000000000..20eea596f4 --- /dev/null +++ b/example_packages/nonintrinsic/src/iso_fortran_env.f90 @@ -0,0 +1,4 @@ +module iso_fortran_env + implicit none + integer, parameter :: ijk = 0 +end module iso_fortran_env From 2dd422261bce78ae3c13b32f5ddb82eb41f4635f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 05:54:39 -0500 Subject: [PATCH 571/799] add to CI --- ci/run_tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index b0e769b73e..987b282449 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -31,6 +31,10 @@ pushd circular_example "$fpm" build popd +pushd nonintrinsic +"$fpm" build +popd + pushd hello_complex "$fpm" build "$fpm" test From 467eff4956594482e68aebc28f391621ac02d73f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 05:58:56 -0500 Subject: [PATCH 572/799] fix use identification fix use identification Update fpm_source_parsing.f90 --- src/fpm_source_parsing.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 12ae899402..904b9db062 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -643,7 +643,8 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na lowercase = lower(trim(adjustl(line))) ! 'use' should be the first string in the adjustl line - use_stmt = index(lowercase,'use')==1; if (.not.use_stmt) return + use_stmt = index(lowercase,'use ')==1 .or. index(lowercase,'use::')==1 .or. index(lowercase,'use,')==1 + if (.not.use_stmt) return colons = index(lowercase,'::') nonintr = 0 intr = 0 From 7f3f7ad4a12e6145fb1d486b6a653094542b7f84 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 13:35:36 +0200 Subject: [PATCH 573/799] Update fpm_source_parsing.f90 --- src/fpm_source_parsing.f90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 904b9db062..f3d9caa31c 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -612,8 +612,6 @@ function parse_sequence(string,t1,t2,t3,t4) result(found) end function parse_sequence -! Process 'USE' statements - ! USE [, intrinsic] :: module_name [, only: only_list] ! USE [, non_intrinsic] :: module_name [, only: only_list] subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_name,error) @@ -693,7 +691,7 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na j=1,size(INTRINSIC_NAMES))]) if (intr>0 .and. .not.has_intrinsic_name) then call file_parse_error(error,f_filename, & - 'module is declared intrinsic but it is not ',i, & + 'module '//module_name//' is declared intrinsic but it is not ',i, & lowercase) return endif @@ -705,5 +703,6 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na end subroutine parse_use_statement + end module fpm_source_parsing From 54528c66dfabdc3eae94ab9e24f0e1da9e080cc5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 13:39:54 +0200 Subject: [PATCH 574/799] fix intrinsic module parsing in the next line --- src/fpm_source_parsing.f90 | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index f3d9caa31c..462cbf1a58 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -690,10 +690,17 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na has_intrinsic_name = any([(index(module_name,trim(INTRINSIC_NAMES(j)))>0, & j=1,size(INTRINSIC_NAMES))]) if (intr>0 .and. .not.has_intrinsic_name) then - call file_parse_error(error,f_filename, & - 'module '//module_name//' is declared intrinsic but it is not ',i, & - lowercase) - return + + ! An intrinsic module was not found. Its name could be in the next line, + ! in which case, we just skip this check. The compiler will do the job if the name is invalid. + + ! Module name was not read: it's in the next line + if (index(module_name,'&')<=0) then + call file_parse_error(error,f_filename, & + 'module '//module_name//' is declared intrinsic but it is not ',i, & + lowercase) + return + endif endif ! Should we treat this as an intrinsic module From 44d2b3ce71826b608c24e77861ee98fa8dafafa8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 1 Jun 2023 10:55:22 -0500 Subject: [PATCH 575/799] cleanup --- src/fpm_source_parsing.f90 | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 462cbf1a58..62719c7cb2 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -646,14 +646,10 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na colons = index(lowercase,'::') nonintr = 0 intr = 0 - intrinsicness: if (colons>3) then - end if intrinsicness + have_colons: if (colons>3) then - ! If declared intrinsic, check that it is true - if (colons>3) then - - ! If there is an intrinsic/non-intrinsic spec + ! there may be an intrinsic/non-intrinsic spec nonintr = index(lowercase(1:colons-1),'non_intrinsic') if (nonintr==0) intr = index(lowercase(1:colons-1),'intrinsic') @@ -684,7 +680,7 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na return end if - end if + end if have_colons ! If declared intrinsic, check that it is true has_intrinsic_name = any([(index(module_name,trim(INTRINSIC_NAMES(j)))>0, & From c966c6bcbc0a628454550650cca27ff9183ca019 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 2 Jun 2023 09:27:14 +0200 Subject: [PATCH 576/799] search `%MSMPI%` also in `get_mpi_runner` --- src/fpm_meta.f90 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 388757ba8a..25f9a97196 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -838,6 +838,7 @@ subroutine get_mpi_runner(command,verbose,error) type(error_t), allocatable, intent(out) :: error character(*), parameter :: try(*) = ['mpiexec ','mpirun ','mpiexec.exe','mpirun.exe '] + character(:), allocatable :: bindir integer :: itri logical :: success @@ -855,6 +856,25 @@ subroutine get_mpi_runner(command,verbose,error) endif end do + ! On windows, also search in %MSMPI_BIN% + if (get_os_type()==OS_WINDOWS) then + ! Check that the runtime is installed + bindir = "" + call get_absolute_path(get_env('MSMPI_BIN'),bindir,error) + if (verbose) print *, '+ %MSMPI_BIN%=',bindir + ! In some environments, variable %MSMPI_BIN% is missing (i.e. in GitHub Action images). + ! Do a second attempt: search for the default location + if (len_trim(bindir)<=0 .or. allocated(error)) then + if (verbose) print *, '+ %MSMPI_BIN% empty, searching C:\Program Files\Microsoft MPI\Bin\ ...' + call get_absolute_path('C:\Program Files\Microsoft MPI\Bin\mpiexec.exe',bindir,error) + endif + if (len_trim(bindir)>0 .and. .not.allocated(error)) then + ! MSMPI_BIN directory found + command%s = join_path(bindir,'mpiexec.exe') + return + endif + endif + ! No valid command found call fatal_error(error,'cannot find a valid mpi runner command') return From c7c421a3098f25327060a9363f9ae1b1309df945 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 2 Jun 2023 05:41:49 -0500 Subject: [PATCH 577/799] Fpm release v0.9.0 (#922) * bump version to 0.9.0 * search MSMPI_BIN also in runner command * Update src/fpm_meta.f90 * Revert "search MSMPI_BIN also in runner command" This reverts commit 28d7e499d00b337dd6b7e457058b1fbb0733e179. --------- Co-authored-by: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index a2c8eeb3d6..90b1712f66 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.8.2" +version = "0.9.0" license = "MIT" author = "fpm maintainers" maintainer = "" From a10bddb088af8a48ad3d95608af2cebd4b6133af Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 8 Jun 2023 09:28:17 +0200 Subject: [PATCH 578/799] allow overriding metapackages with standard deps --- src/fpm/manifest/dependency.f90 | 79 ++++++++++++++++++++++----------- src/fpm/manifest/meta.f90 | 38 +++++++++++++--- 2 files changed, 85 insertions(+), 32 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 4c4282500b..8d1b129fa9 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -29,7 +29,8 @@ module fpm_manifest_dependency use fpm_toml, only: toml_table, toml_key, toml_stat, get_value, check_keys use fpm_filesystem, only: windows_path, join_path use fpm_environment, only: get_os_type, OS_WINDOWS - use fpm_manifest_metapackages, only: metapackage_config_t, is_meta_package, new_meta_config + use fpm_manifest_metapackages, only: metapackage_config_t, is_meta_package, new_meta_config, & + metapackage_request_t, new_meta_request use fpm_versioning, only: version_t, new_version implicit none private @@ -223,46 +224,74 @@ subroutine new_dependencies(deps, table, root, meta, error) type(toml_table), pointer :: node type(toml_key), allocatable :: list(:) - logical, allocatable :: non_meta(:) + type(dependency_config_t), allocatable :: all_deps(:) + type(metapackage_request_t) :: meta_request + logical, allocatable :: is_meta(:) + logical :: metapackages_allowed integer :: idep, stat, ndep call table%get_keys(list) ! An empty table is okay if (size(list) < 1) return - !> Count non-metapackage dependencies, and parse metapackage config - if (present(meta)) then - ndep = 0 - do idep = 1, size(list) - if (is_meta_package(list(idep)%key)) cycle - ndep = ndep+1 - end do + !> Flag dependencies that should be treated as metapackages + metapackages_allowed = present(meta) + allocate(is_meta(size(list)),source=.false.) + allocate(all_deps(size(list))) - !> Return metapackages config from this node - call new_meta_config(meta, table, error) - if (allocated(error)) return - else - ndep = size(list) - end if - - ! Generate non-metapackage dependencies - allocate(deps(ndep)) - ndep = 0 + !> Parse all meta- and non-metapackage dependencies do idep = 1, size(list) - if (present(meta) .and. is_meta_package(list(idep)%key)) cycle - - ndep = ndep+1 - call get_value(table, list(idep)%key, node, stat=stat) if (stat /= toml_stat%success) then call syntax_error(error, "Dependency "//list(idep)%key//" must be a table entry") exit end if - call new_dependency(deps(ndep), node, root, error) - if (allocated(error)) exit + + ! Try to parse as a standard dependency + call new_dependency(all_deps(idep), node, root, error) + + is_standard_dependency: if (.not.allocated(error)) then + + ! If a valid git/local config is found, use it always + is_meta(idep) = .false. + + elseif (metapackages_allowed .and. is_meta_package(list(idep)%key)) then + + !> Metapackage name: Check if this is a valid metapackage request + call new_meta_request(meta_request, list(idep)%key, table, error=error) + + !> Neither a standard dep nor a metapackage + if (allocated(error)) return + + !> Valid meta dependency + is_meta(idep) = .true. + + else + + !> Not a standard dependency and not a metapackage: dump an error + call syntax_error(error, "Dependency "//list(idep)%key//" cannot be parsed. Check input format") + return + + endif is_standard_dependency + + end do + + ! Non-meta dependencies + ndep = count(.not.is_meta) + + ! Finalize standard dependencies + allocate(deps(ndep)) + ndep = 0 + do idep = 1, size(list) + if (is_meta(idep)) cycle + ndep = ndep+1 + deps(ndep) = all_deps(idep) end do + ! Finalize meta dependencies + if (metapackages_allowed) call new_meta_config(meta,table,is_meta,error) + end subroutine new_dependencies !> Write information on instance diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index 17261960eb..f4b3dadfa6 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -16,6 +16,7 @@ module fpm_manifest_metapackages private public :: metapackage_config_t, new_meta_config, is_meta_package + public :: metapackage_request_t, new_meta_request !> Configuration data for a single metapackage request @@ -95,7 +96,7 @@ subroutine request_parse(self, version_request, error) end subroutine request_parse !> Construct a new metapackage request from the dependencies table - subroutine new_request(self, key, table, error) + subroutine new_meta_request(self, key, table, meta_allowed, error) type(metapackage_request_t), intent(out) :: self @@ -105,12 +106,16 @@ subroutine new_request(self, key, table, error) !> Instance of the TOML data structure type(toml_table), intent(inout) :: table + !> List of keys allowed to be metapackages + logical, intent(in), optional :: meta_allowed(:) + !> Error handling type(error_t), allocatable, intent(out) :: error integer :: stat,i character(len=:), allocatable :: value + logical, allocatable :: allow_meta(:) type(toml_key), allocatable :: keys(:) call request_destroy(self) @@ -127,7 +132,23 @@ subroutine new_request(self, key, table, error) call table%get_keys(keys) + !> Set list of entries that are allowed to be metapackages + if (present(meta_allowed)) then + if (size(meta_allowed)/=size(keys)) then + call fatal_error(error,"Internal error: list of metapackage-enable entries does not match table size") + return + end if + allow_meta = meta_allowed + else + allocate(allow_meta(size(keys)),source=.true.) + endif + + do i=1,size(keys) + + ! Skip standard dependencies + if (.not.meta_allowed(i)) cycle + if (keys(i)%key==key) then call get_value(table, key, value) if (.not. allocated(value)) then @@ -143,10 +164,10 @@ subroutine new_request(self, key, table, error) ! Key is not present, metapackage not requested return - end subroutine new_request + end subroutine new_meta_request !> Construct a new build configuration from a TOML data structure - subroutine new_meta_config(self, table, error) + subroutine new_meta_config(self, table, meta_allowed, error) !> Instance of the build configuration type(metapackage_config_t), intent(out) :: self @@ -154,6 +175,9 @@ subroutine new_meta_config(self, table, error) !> Instance of the TOML data structure type(toml_table), intent(inout) :: table + !> List of keys allowed to be metapackages + logical, intent(in) :: meta_allowed(:) + !> Error handling type(error_t), allocatable, intent(out) :: error @@ -161,16 +185,16 @@ subroutine new_meta_config(self, table, error) !> The toml table is not checked here because it already passed !> the "new_dependencies" check - call new_request(self%openmp, "openmp", table, error) + call new_meta_request(self%openmp, "openmp", table, meta_allowed, error) if (allocated(error)) return - call new_request(self%stdlib, "stdlib", table, error) + call new_meta_request(self%stdlib, "stdlib", table, meta_allowed, error) if (allocated(error)) return - call new_request(self%minpack, "minpack", table, error) + call new_meta_request(self%minpack, "minpack", table, meta_allowed, error) if (allocated(error)) return - call new_request(self%mpi, "mpi", table, error) + call new_meta_request(self%mpi, "mpi", table, meta_allowed, error) if (allocated(error)) return end subroutine new_meta_config From f8c728278af48ad474afd7da883675977eb82fe8 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 8 Jun 2023 09:50:06 +0200 Subject: [PATCH 579/799] fix logic --- src/fpm/manifest/dependency.f90 | 33 +++++++++++++-------------------- src/fpm/manifest/meta.f90 | 2 +- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 8d1b129fa9..75f5f5d10d 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -242,38 +242,31 @@ subroutine new_dependencies(deps, table, root, meta, error) !> Parse all meta- and non-metapackage dependencies do idep = 1, size(list) + ! Check if this is a standard dependency node call get_value(table, list(idep)%key, node, stat=stat) - if (stat /= toml_stat%success) then - call syntax_error(error, "Dependency "//list(idep)%key//" must be a table entry") - exit - end if - - ! Try to parse as a standard dependency - call new_dependency(all_deps(idep), node, root, error) - - is_standard_dependency: if (.not.allocated(error)) then - - ! If a valid git/local config is found, use it always - is_meta(idep) = .false. + is_standard_dependency: if (stat /= toml_stat%success) then - elseif (metapackages_allowed .and. is_meta_package(list(idep)%key)) then - - !> Metapackage name: Check if this is a valid metapackage request + ! See if it can be a valid metapackage name call new_meta_request(meta_request, list(idep)%key, table, error=error) !> Neither a standard dep nor a metapackage - if (allocated(error)) return + if (allocated(error)) then + call syntax_error(error, "Dependency "//list(idep)%key//" is not a valid metapackage or a table entry") + return + endif !> Valid meta dependency is_meta(idep) = .true. else - !> Not a standard dependency and not a metapackage: dump an error - call syntax_error(error, "Dependency "//list(idep)%key//" cannot be parsed. Check input format") - return + ! Parse as a standard dependency + is_meta(idep) = .false. + + call new_dependency(all_deps(idep), node, root, error) + if (allocated(error)) return - endif is_standard_dependency + end if is_standard_dependency end do diff --git a/src/fpm/manifest/meta.f90 b/src/fpm/manifest/meta.f90 index f4b3dadfa6..3719067030 100644 --- a/src/fpm/manifest/meta.f90 +++ b/src/fpm/manifest/meta.f90 @@ -147,7 +147,7 @@ subroutine new_meta_request(self, key, table, meta_allowed, error) do i=1,size(keys) ! Skip standard dependencies - if (.not.meta_allowed(i)) cycle + if (.not.allow_meta(i)) cycle if (keys(i)%key==key) then call get_value(table, key, value) From 9a46ce4e52d6805ec9f9f39885f114b1faa09b79 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 8 Jun 2023 10:20:21 +0200 Subject: [PATCH 580/799] add metapackage overriding tests --- test/fpm_test/test_package_dependencies.f90 | 69 ++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 75a1cb255c..4f645750b5 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -7,6 +7,8 @@ module test_package_dependencies use fpm_os, only: get_current_directory use fpm_dependency use fpm_manifest_dependency + use fpm_manifest_metapackages, only: metapackage_config_t + use fpm_manifest, only: package_config_t, get_package_data use fpm_toml use fpm_settings, only: fpm_global_settings, get_registry_settings, get_global_settings use fpm_downloader, only: downloader_t @@ -45,10 +47,11 @@ subroutine collect_package_dependencies(tests) & new_unittest("status-after-load", test_status), & & new_unittest("add-dependencies", test_add_dependencies), & & new_unittest("update-dependencies", test_update_dependencies), & + & new_unittest("metapackage-override", test_metapackage_override), & & new_unittest("do-not-update-dependencies", test_non_updated_dependencies), & & new_unittest("registry-dir-not-found", registry_dir_not_found, should_fail=.true.), & & new_unittest("no-versions-in-registry", no_versions_in_registry, should_fail=.true.), & - & new_unittest("local-registry-specified-version-not-found", local_registry_specified_version_not_found, should_fail=.true.), & + & new_unittest("local-registry-specified-version-not-found", local_registry_specified_version_not_found, should_fail=.true.), & & new_unittest("local-registry-specified-no-manifest", local_registry_specified_no_manifest, should_fail=.true.), & & new_unittest("local-registry-specified-has-manifest", local_registry_specified_has_manifest), & & new_unittest("local-registry-specified-not-a-dir", local_registry_specified_not_a_dir, should_fail=.true.), & @@ -421,6 +424,70 @@ subroutine test_update_dependencies(error) end subroutine test_update_dependencies + + !> Test that a metapackage is overridden if a regular dependency is provided + subroutine test_metapackage_override(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: manifest + type(toml_table), pointer :: ptr + type(dependency_config_t), allocatable :: deps(:) + type(metapackage_config_t) :: meta + logical :: found + integer :: i + + ! Create a dummy manifest, with a standard git dependency for stdlib + manifest = toml_table() + call add_table(manifest, "stdlib", ptr) + call set_value(ptr, "git", "https://github.com/fortran-lang/stdlib") + call set_value(ptr, "branch", "stdlib-fpm") + + ! Load dependencies from manifest + call new_dependencies(deps, manifest, meta=meta, error=error) + if (allocated(error)) return + + ! Check that stdlib is in the regular dependency list + found = .false. + do i=1,size(deps) + if (deps(i)%name=="stdlib") found = .true. + end do + + if (.not.found) then + call test_failed(error,"standard git-based dependency for stdlib not recognized") + return + end if + call manifest%destroy() + + + ! Create a dummy manifest, with a version-based metapackage dependency for stdlib + manifest = toml_table() + call set_value(manifest, "stdlib", "*") + + ! Load dependencies from manifest + call new_dependencies(deps, manifest, meta=meta, error=error) + if (allocated(error)) return + + ! Check that stdlib is in the metapackage config and not the standard dependencies + found = .false. + do i=1,size(deps) + if (deps(i)%name=="stdlib") found = .true. + end do + + if (found) then + call test_failed(error,"metapackage dependency for stdlib should not be in the tree") + return + end if + call manifest%destroy() + + if (.not.meta%stdlib%on) then + call test_failed(error,"metapackage dependency for stdlib should be in the metapackage config") + return + end if + + end subroutine test_metapackage_override + !> Directories for namespace and package name not found in path registry. subroutine registry_dir_not_found(error) type(error_t), allocatable, intent(out) :: error From c80cdfda1edfd96a6e339a966baf01c7a5743095 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 8 Jun 2023 10:32:17 +0200 Subject: [PATCH 581/799] add `mpi` and `mpi_f08` to the list of external modules --- src/fpm_meta.f90 | 51 +++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 25f9a97196..c8fd4171de 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -39,15 +39,16 @@ module fpm_meta !> Package version (if supported) type(version_t), allocatable :: version - logical :: has_link_libraries = .false. - logical :: has_link_flags = .false. - logical :: has_build_flags = .false. - logical :: has_fortran_flags = .false. - logical :: has_c_flags = .false. - logical :: has_cxx_flags = .false. - logical :: has_include_dirs = .false. - logical :: has_dependencies = .false. - logical :: has_run_command = .false. + logical :: has_link_libraries = .false. + logical :: has_link_flags = .false. + logical :: has_build_flags = .false. + logical :: has_fortran_flags = .false. + logical :: has_c_flags = .false. + logical :: has_cxx_flags = .false. + logical :: has_include_dirs = .false. + logical :: has_dependencies = .false. + logical :: has_run_command = .false. + logical :: has_external_modules = .false. !> List of compiler flags and options to be added type(string_t) :: flags @@ -58,6 +59,7 @@ module fpm_meta type(string_t) :: run_command type(string_t), allocatable :: incl_dirs(:) type(string_t), allocatable :: link_libs(:) + type(string_t), allocatable :: external_modules(:) !> Special fortran features type(fortran_features_t), allocatable :: fortran @@ -120,15 +122,16 @@ end function MPI_TYPE_NAME elemental subroutine destroy(this) class(metapackage_t), intent(inout) :: this - this%has_link_libraries = .false. - this%has_link_flags = .false. - this%has_build_flags = .false. - this%has_fortran_flags = .false. - this%has_c_flags = .false. - this%has_cxx_flags = .false. - this%has_include_dirs = .false. - this%has_dependencies = .false. - this%has_run_command = .false. + this%has_link_libraries = .false. + this%has_link_flags = .false. + this%has_build_flags = .false. + this%has_fortran_flags = .false. + this%has_c_flags = .false. + this%has_cxx_flags = .false. + this%has_include_dirs = .false. + this%has_dependencies = .false. + this%has_run_command = .false. + this%has_external_modules = .false. if (allocated(this%fortran)) deallocate(this%fortran) if (allocated(this%version)) deallocate(this%version) @@ -141,6 +144,7 @@ elemental subroutine destroy(this) if (allocated(this%link_libs)) deallocate(this%link_libs) if (allocated(this%dependency)) deallocate(this%dependency) if (allocated(this%incl_dirs)) deallocate(this%incl_dirs) + if (allocated(this%external_modules)) deallocate(this%external_modules) end subroutine destroy @@ -327,6 +331,10 @@ subroutine resolve_model(self,model,error) model%include_dirs = [model%include_dirs,self%incl_dirs] end if + if (self%has_external_modules) then + model%external_modules = [model%external_modules,self%external_modules] + end if + end subroutine resolve_model subroutine resolve_package_config(self,package,error) @@ -467,11 +475,9 @@ subroutine init_mpi(this,compiler,error) integer :: wcfit(3),mpilib(3),ic,icpp,i logical :: found - !> Cleanup call destroy(this) - !> Get all candidate MPI wrappers call mpi_wrappers(compiler,fort_wrappers,c_wrappers,cpp_wrappers) if (verbose) print 1, size(fort_wrappers),size(c_wrappers),size(cpp_wrappers) @@ -522,6 +528,11 @@ subroutine init_mpi(this,compiler,error) end if + !> Not all MPI implementations offer modules mpi and mpi_f08: hence, include them + !> to the list of external modules, so they won't be requested as standard source files + this%has_external_modules = .true. + this%external_modules = [string_t("mpi"),string_t("mpi_f08")] + 1 format('MPI wrappers found: fortran=',i0,' c=',i0,' c++=',i0) end subroutine init_mpi From e5a45626c146d8972ed819c75bb9d69c2c2a1b83 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 9 Jun 2023 21:24:13 +0700 Subject: [PATCH 582/799] Fix typo --- src/fpm/cmd/publish.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 22c283dac8..c92cc5ff14 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -47,7 +47,7 @@ subroutine cmd_publish(settings) !> Checks before uploading the package. if (.not. allocated(package%license)) call fpm_stop(1, 'No license specified in fpm.toml.') if (.not. package%build%module_naming) call fpm_stop(1, 'The package does not meet the module naming requirements. '// & - & 'Please set "module_naming = true" in fpm.toml [build] or specify a custom module prefix.') + & 'Please set "module-naming = true" in fpm.toml [build] or specify a custom module prefix.') if (.not. allocated(version)) call fpm_stop(1, 'No version specified in fpm.toml.') if (version%s() == '0') call fpm_stop(1, 'Invalid version: "'//version%s()//'".') if (.not. exists('fpm.toml')) call fpm_stop(1, "Cannot find 'fpm.toml' file. Are you in the project root?") From 1075f02ec7c85e0b6af157c512550f754ae117e4 Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Fri, 9 Jun 2023 23:55:59 -0400 Subject: [PATCH 583/799] document run(3f) and markdown errata --- src/fpm_filesystem.F90 | 83 +++++++++++++++++++++++++++++++++++------- src/fpm_strings.f90 | 18 ++++----- 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 97feb1801b..d5637357d1 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -774,31 +774,32 @@ subroutine filewrite(filename,filedata) end subroutine filewrite -function which(command) result(pathname) +!>AUTHOR: John S. Urban +!!LICENSE: Public Domain !> -!!##NAME +!!##Name !! which(3f) - [M_io:ENVIRONMENT] given a command name find the pathname by searching !! the directories in the environment variable $PATH !! (LICENSE:PD) !! -!!##SYNTAX +!!##Syntax !! function which(command) result(pathname) !! !! character(len=*),intent(in) :: command !! character(len=:),allocatable :: pathname !! -!!##DESCRIPTION +!!##Description !! Given a command name find the first file with that name in the directories !! specified by the environment variable $PATH. !! -!!##OPTIONS +!!##options !! COMMAND the command to search for !! -!!##RETURNS +!!##Returns !! PATHNAME the first pathname found in the current user path. Returns blank !! if the command is not found. !! -!!##EXAMPLE +!!##Example !! !! Sample program: !! @@ -812,11 +813,7 @@ function which(command) result(pathname) !! write(*,*)'install is ',which('install') !! end program demo_which !! -!!##AUTHOR -!! John S. Urban -!!##LICENSE -!! Public Domain - +function which(command) result(pathname) character(len=*),intent(in) :: command character(len=:),allocatable :: pathname, checkon, paths(:), exts(:) integer :: i, j @@ -854,8 +851,66 @@ function which(command) result(pathname) enddo SEARCH end function which -!> echo command string and pass it to the system for execution -!call run(cmd,echo=.false.,exitstat=exitstat,verbose=.false.,redirect='') +!>AUTHOR: fpm(1) contributors +!!LICENSE: MIT +!> +!!##Name +!! run(3f) - execute specified system command and selectively echo +!! command and output to a file and/or stdout. +!! (LICENSE:MIT) +!! +!!##Syntax +!! subroutine run(cmd,echo,exitstat,verbose,redirect) +!! +!! character(len=*), intent(in) :: cmd +!! logical,intent(in),optional :: echo +!! integer, intent(out),optional :: exitstat +!! logical, intent(in), optional :: verbose +!! character(*), intent(in), optional :: redirect +!! +!!##Description +!! Execute the specified system command. Optionally +!! +!! + echo the command before execution +!! + return the system exit status of the command. +!! + redirect the output of the command to a file. +!! + echo command output to stdout +!! +!! Calling run(3f) is preferred to direct calls to +!! execute_command_line(3f) in the fpm(1) source to provide a standard +!! interface where output modes can be specified. +!! +!!##Options +!! CMD System command to execute +!! ECHO Whether to echo the command being executed or not +!! Defaults to .TRUE. . +!! VERBOSE Whether to redirect the command output to a null device or not +!! Defaults to .TRUE. . +!! REDIRECT Filename to redirect stdout and stderr of the command into. +!! If generated it is closed before run(3f) returns. +!! EXITSTAT The system exit status of the command when supported by +!! the system. If not present and a non-zero status is +!! generated program termination occurs. +!! +!!##Example +!! +!! Sample program: +!! +!! Checking the error message and counting lines: +!! +!! program demo_run +!! use fpm_filesystem, only : run +!! implicit none +!! logical,parameter :: T=.true., F=.false. +!! integer :: exitstat +!! character(len=:),allocatable :: cmd +!! cmd='ls -ltrasd *.md' +!! call run(cmd) +!! call run(cmd,exitstat=exitstat) +!! call run(cmd,echo=F) +!! call run(cmd,verbose=F) +!! end program demo_run +!! subroutine run(cmd,echo,exitstat,verbose,redirect) character(len=*), intent(in) :: cmd logical,intent(in),optional :: echo diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index 404a7dc6f5..e478f4dba6 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -236,9 +236,9 @@ pure function fnv_1a_string_t(input, seed) result(hash) end function fnv_1a_string_t - !>Author: John S. Urban - !!License: Public Domain - !! Changes a string to lowercase over optional specified column range +!>Author: John S. Urban +!!License: Public Domain +!! Changes a string to lowercase over optional specified column range elemental pure function lower(str,begin,end) result (string) character(*), intent(In) :: str @@ -624,8 +624,9 @@ pure function join(str,sep,trm,left,right,start,end) result (string) if(present(end))string=string//end end function join -!>##AUTHOR John S. Urban -!!##LICENSE Public Domain +!>AUTHOR: John S. Urban +!!LICENSE: Public Domain +!> !!## NAME !! glob(3f) - [fpm_strings:COMPARE] compare given string for match to !! pattern which may contain wildcard characters @@ -1259,6 +1260,8 @@ subroutine remove_newline_characters(string) end subroutine remove_newline_characters +!>AUTHOR: John S. Urban +!!LICENSE: Public Domain !> !!### NAME !! notabs(3f) - [fpm_strings:NONALPHA] expand tab characters @@ -1316,11 +1319,6 @@ end subroutine remove_newline_characters !!### SEE ALSO !! GNU/Unix commands expand(1) and unexpand(1) !! -!!### AUTHOR -!! John S. Urban -!! -!!### LICENSE -!! Public Domain elemental impure subroutine notabs(instr,outstr,ilen) ! ident_31="@(#)fpm_strings::notabs(3f): convert tabs to spaces while maintaining columns, remove CRLF chars" From 2ada9fae44a8ed62cde35c636f79dbb81f2fa282 Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Sat, 10 Jun 2023 00:00:23 -0400 Subject: [PATCH 584/799] start example directory --- example/demo_run.f90 | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 example/demo_run.f90 diff --git a/example/demo_run.f90 b/example/demo_run.f90 new file mode 100644 index 0000000000..c22a145000 --- /dev/null +++ b/example/demo_run.f90 @@ -0,0 +1,47 @@ +program demo_run +use fpm_filesystem, only: run +implicit none +integer :: exitstat +character(len=:), allocatable :: cmd +logical, parameter T = .true., F = .false. +cmd = 'ls -ltrasd *.md' + + call run(cmd) + call paws('default options (ie. echo=T verbose=T)') + + call run(cmd, exitstat=exitstat) + write (*, *) 'exitstat=', exitstat + call paws('exitstat') + + call run(cmd, echo=F) + call paws('echo=F') + + call run(cmd, verbose=F) + call paws('verbose=F') + + call run(cmd, verbose=F, echo=F) + call paws('verbose=F echo=F') + + call run(cmd, redirect='_scratch') + call paws('redirect="_scratch"') + + call run(cmd, redirect='_scratch', verbose=F) + call paws('redirect="_scratch" verbose=F') + + call run(cmd, redirect='_scratch', verbose=T) + call paws('redirect="_scratch" verbose=T') + +contains + +subroutine paws(str) +character(len=*), intent(in) :: str +character(len=1) :: chr +integer :: iostat + + write (*, '(a,": ")', advance='no') str + read (*, '(a)', iostat=iostat) chr + write (*, '(a)') repeat('-', 60) + +end subroutine paws + +end program demo_run From 69edccf1ffb69186e077953dc57dd38b9694b52f Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Sat, 10 Jun 2023 00:20:40 -0400 Subject: [PATCH 585/799] correct example --- example/demo_run.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/demo_run.f90 b/example/demo_run.f90 index c22a145000..90bebb9e32 100644 --- a/example/demo_run.f90 +++ b/example/demo_run.f90 @@ -3,7 +3,7 @@ program demo_run implicit none integer :: exitstat character(len=:), allocatable :: cmd -logical, parameter T = .true., F = .false. +logical, parameter :: T = .true., F = .false. cmd = 'ls -ltrasd *.md' call run(cmd) From 98eb02134fa42dedccd4404817bbb11464151268 Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Sat, 10 Jun 2023 00:30:05 -0400 Subject: [PATCH 586/799] remove example/ --- example/demo_run.f90 | 47 -------------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 example/demo_run.f90 diff --git a/example/demo_run.f90 b/example/demo_run.f90 deleted file mode 100644 index 90bebb9e32..0000000000 --- a/example/demo_run.f90 +++ /dev/null @@ -1,47 +0,0 @@ -program demo_run -use fpm_filesystem, only: run -implicit none -integer :: exitstat -character(len=:), allocatable :: cmd -logical, parameter :: T = .true., F = .false. -cmd = 'ls -ltrasd *.md' - - call run(cmd) - call paws('default options (ie. echo=T verbose=T)') - - call run(cmd, exitstat=exitstat) - write (*, *) 'exitstat=', exitstat - call paws('exitstat') - - call run(cmd, echo=F) - call paws('echo=F') - - call run(cmd, verbose=F) - call paws('verbose=F') - - call run(cmd, verbose=F, echo=F) - call paws('verbose=F echo=F') - - call run(cmd, redirect='_scratch') - call paws('redirect="_scratch"') - - call run(cmd, redirect='_scratch', verbose=F) - call paws('redirect="_scratch" verbose=F') - - call run(cmd, redirect='_scratch', verbose=T) - call paws('redirect="_scratch" verbose=T') - -contains - -subroutine paws(str) -character(len=*), intent(in) :: str -character(len=1) :: chr -integer :: iostat - - write (*, '(a,": ")', advance='no') str - read (*, '(a)', iostat=iostat) chr - write (*, '(a)') repeat('-', 60) - -end subroutine paws - -end program demo_run From 3c0bbcb5374b5587f02b7e9528f7d1c8904ec2d4 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 11 Jun 2023 11:53:23 +0200 Subject: [PATCH 587/799] do not preprocess with `trim(adjustl(line))` --- src/fpm_source_parsing.f90 | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index 62719c7cb2..ed5746c90d 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -615,8 +615,13 @@ end function parse_sequence ! USE [, intrinsic] :: module_name [, only: only_list] ! USE [, non_intrinsic] :: module_name [, only: only_list] subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_name,error) - character(*), intent(in) :: f_filename,line - integer, intent(in) :: i ! line number + + !> Current file name and line number (for error messaging) + character(*), intent(in) :: f_filename + integer, intent(in) :: i + + !> The line being parsed. MUST BE preprocessed with trim(adjustl() + character(*), intent(in) :: line logical, intent(out) :: use_stmt,is_intrinsic character(:), allocatable, intent(out) :: module_name type(error_t), allocatable, intent(out) :: error @@ -629,7 +634,7 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na 'ieee_features ', & 'omp_lib '] - character(len=:), allocatable :: lowercase,temp_string + character(len=:), allocatable :: temp_string integer :: colons,intr,nonintr,j,stat logical :: has_intrinsic_name @@ -637,28 +642,31 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na is_intrinsic = .false. if (len_trim(line)<=0) return - ! Preprocess: lowercase, remove heading spaces - lowercase = lower(trim(adjustl(line))) + ! Quick check that the line is preprocessed + if (line(1:1)==' ') then + call fatal_error(error,'internal_error: source file line is not trim(adjustl()) on input to parse_use_statement') + return + end if ! 'use' should be the first string in the adjustl line - use_stmt = index(lowercase,'use ')==1 .or. index(lowercase,'use::')==1 .or. index(lowercase,'use,')==1 + use_stmt = index(line,'use ')==1 .or. index(line,'use::')==1 .or. index(line,'use,')==1 if (.not.use_stmt) return - colons = index(lowercase,'::') + colons = index(line,'::') nonintr = 0 intr = 0 have_colons: if (colons>3) then ! there may be an intrinsic/non-intrinsic spec - nonintr = index(lowercase(1:colons-1),'non_intrinsic') - if (nonintr==0) intr = index(lowercase(1:colons-1),'intrinsic') + nonintr = index(line(1:colons-1),'non_intrinsic') + if (nonintr==0) intr = index(line(1:colons-1),'intrinsic') - temp_string = split_n(lowercase,delims=':',n=2,stat=stat) + temp_string = split_n(line,delims=':',n=2,stat=stat) if (stat /= 0) then call file_parse_error(error,f_filename, & 'unable to find used module name',i, & - lowercase,colons) + line,colons) return end if @@ -666,17 +674,17 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na if (stat /= 0) then call file_parse_error(error,f_filename, & 'unable to find used module name',i, & - lowercase) + line) return end if else - module_name = split_n(lowercase,n=2,delims=' ,',stat=stat) + module_name = split_n(line,n=2,delims=' ,',stat=stat) if (stat /= 0) then call file_parse_error(error,f_filename, & 'unable to find used module name',i, & - lowercase) + line) return end if @@ -694,7 +702,7 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na if (index(module_name,'&')<=0) then call file_parse_error(error,f_filename, & 'module '//module_name//' is declared intrinsic but it is not ',i, & - lowercase) + line) return endif endif From af077f2f81f67f6710242d188361dca9240eecfc Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 11 Jun 2023 11:58:00 +0200 Subject: [PATCH 588/799] document arguments --- src/fpm_source_parsing.f90 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/fpm_source_parsing.f90 b/src/fpm_source_parsing.f90 index ed5746c90d..f303a1c2cf 100644 --- a/src/fpm_source_parsing.f90 +++ b/src/fpm_source_parsing.f90 @@ -622,8 +622,17 @@ subroutine parse_use_statement(f_filename,i,line,use_stmt,is_intrinsic,module_na !> The line being parsed. MUST BE preprocessed with trim(adjustl() character(*), intent(in) :: line - logical, intent(out) :: use_stmt,is_intrinsic + + !> Does this line contain a `use` statement? + logical, intent(out) :: use_stmt + + !> Is the module in this statement intrinsic? + logical, intent(out) :: is_intrinsic + + !> used module name character(:), allocatable, intent(out) :: module_name + + !> Error handling type(error_t), allocatable, intent(out) :: error character(15), parameter :: INTRINSIC_NAMES(*) = & From 6e3ca2b3fcac1f7aebf8d83b1dc4208d57ec980a Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 11 Jun 2023 12:01:02 +0200 Subject: [PATCH 589/799] fix error messages in test --- test/fpm_test/test_source_parsing.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fpm_test/test_source_parsing.f90 b/test/fpm_test/test_source_parsing.f90 index 41d7db3a84..9a5f5802ec 100644 --- a/test/fpm_test/test_source_parsing.f90 +++ b/test/fpm_test/test_source_parsing.f90 @@ -240,22 +240,22 @@ subroutine test_nonintrinsic_modules_used(error) end if if (.not. ('ieee_arithmetic' .in. f_source%modules_used)) then - call test_failed(error,'Non-Intrinsic module found in modules_used') + call test_failed(error,'Non-Intrinsic module not found in modules_used') return end if if (.not. ('ieee_exceptions' .in. f_source%modules_used)) then - call test_failed(error,'Non-Intrinsic module found in modules_used') + call test_failed(error,'Non-Intrinsic module not found in modules_used') return end if if (.not. ('ieee_features' .in. f_source%modules_used)) then - call test_failed(error,'Non-Intrinsic module found in modules_used') + call test_failed(error,'Non-Intrinsic module not found in modules_used') return end if if (.not. ('my_module' .in. f_source%modules_used)) then - call test_failed(error,'Non-Intrinsic module found in modules_used') + call test_failed(error,'Non-Intrinsic module not found in modules_used') return end if From fde7e7cdc8fe6e6c825ca3595b639eefcb38282f Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 14 Jun 2023 14:28:26 +0200 Subject: [PATCH 590/799] MPI: check run command only on `run` and `test` apps --- src/fpm_meta.f90 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c8fd4171de..153a798f23 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -412,6 +412,15 @@ subroutine add_metapackage_model(model,package,settings,name,error) call meta%resolve(settings,error) if (allocated(error)) return + ! If we need to run executables, there shouold be an MPI runner + if (name=="mpi") then + select type (settings) + class is (fpm_run_settings) ! run, test + if (.not.meta%has_run_command) & + call fatal_error(error,"cannot find a valid mpi runner on the local host") + end select + endif + end subroutine add_metapackage_model !> Resolve all metapackages into the package config @@ -1006,8 +1015,10 @@ subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cx !> Add default run command, if present this%run_command = mpi_wrapper_query(mpilib,fort_wrapper,'runner',verbose,error) - if (allocated(error)) return - this%has_run_command = len_trim(this%run_command)>0 + this%has_run_command = (len_trim(this%run_command)>0) .and. .not.allocated(error) + + !> Do not trigger a fatal error here if run command is missing + if (allocated(error)) deallocate(error) contains From 08dbd956341c40518572b331d72dd98f16a79eb6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 14 Jun 2023 14:54:00 +0200 Subject: [PATCH 591/799] fix invalid verbosity --- src/fpm_meta.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 153a798f23..e3cb5cbcfb 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -864,7 +864,7 @@ subroutine get_mpi_runner(command,verbose,error) ! Try several commands do itri=1,size(try) - call find_command_location(trim(try(itri)),command%s,verbose=.true.,error=error) + call find_command_location(trim(try(itri)),command%s,verbose=verbose,error=error) if (allocated(error)) cycle ! Success! From 0c68c3670b58c046948bee9f74c449301eb73578 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 14 Jun 2023 15:06:24 +0200 Subject: [PATCH 592/799] remove verbosity --- src/fpm_meta.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index e3cb5cbcfb..1ddf969693 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -1074,7 +1074,7 @@ subroutine mpi_compiler_match(language,wrappers,compiler,which_one,mpilib,error) select case (language) case (LANG_FORTRAN) ! Build compiler type. The ID is created based on the Fortran name - call new_compiler(mpi_compiler,screen%s,'','',echo=.true.,verbose=.true.) + call new_compiler(mpi_compiler,screen%s,'','',echo=.true.,verbose=.false.) ! Fortran match found! if (mpi_compiler%id == compiler%id) then From 535f5ffe17c7aa64819bac0a3f88b687be931c86 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 14 Jun 2023 09:15:13 -0500 Subject: [PATCH 593/799] Update src/fpm_meta.f90 Co-authored-by: Minh Dao <43783196+minhqdao@users.noreply.github.com> --- src/fpm_meta.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 1ddf969693..fa113f0c7a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -412,7 +412,7 @@ subroutine add_metapackage_model(model,package,settings,name,error) call meta%resolve(settings,error) if (allocated(error)) return - ! If we need to run executables, there shouold be an MPI runner + ! If we need to run executables, there should be an MPI runner if (name=="mpi") then select type (settings) class is (fpm_run_settings) ! run, test From 0e5ad5ca3a7eb9dcdf58fed3db2ae041e1dbf009 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 14 Jun 2023 22:01:21 +0200 Subject: [PATCH 594/799] do not use `error` for mpi runner search --- src/fpm_meta.f90 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 1ddf969693..d0cb3a7737 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -976,6 +976,7 @@ subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cx type(error_t), allocatable, intent(out) :: error type(version_t) :: version + type(error_t), allocatable :: runner_error ! Cleanup structure call destroy(this) @@ -1014,11 +1015,8 @@ subroutine init_mpi_from_wrappers(this,compiler,mpilib,fort_wrapper,c_wrapper,cx end if !> Add default run command, if present - this%run_command = mpi_wrapper_query(mpilib,fort_wrapper,'runner',verbose,error) - this%has_run_command = (len_trim(this%run_command)>0) .and. .not.allocated(error) - - !> Do not trigger a fatal error here if run command is missing - if (allocated(error)) deallocate(error) + this%run_command = mpi_wrapper_query(mpilib,fort_wrapper,'runner',verbose,runner_error) + this%has_run_command = (len_trim(this%run_command)>0) .and. .not.allocated(runner_error) contains From 83d0c4ab6cf2e8487f8a89d72f9daf528accd49d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 15 Jun 2023 14:45:18 +0700 Subject: [PATCH 595/799] Add verbose output to git_archive --- src/fpm/cmd/publish.f90 | 3 +-- src/fpm/git.f90 | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index c92cc5ff14..121316e7b4 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -65,7 +65,7 @@ subroutine cmd_publish(settings) end do tmp_file = get_temp_filename() - call git_archive('.', tmp_file, error) + call git_archive('.', tmp_file, 'HEAD', settings%verbose, error) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Archive error: '//error%message) upload_data = [ & @@ -91,7 +91,6 @@ subroutine cmd_publish(settings) end if if (settings%verbose) then - print *, '' call print_upload_data(upload_data) print *, '' end if diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index ad86ca3f73..f8238b2075 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -308,31 +308,51 @@ subroutine info(self, unit, verbosity) end subroutine info !> Archive a folder using `git archive`. - subroutine git_archive(source, destination, error) + subroutine git_archive(source, destination, ref, verbose, error) !> Directory to archive. character(*), intent(in) :: source !> Destination of the archive. character(*), intent(in) :: destination + !> (Symbolic) Reference to be archived. + character(*), intent(in) :: ref + !> Whether to print verbose output. + logical, intent(in) :: verbose !> Error handling. type(error_t), allocatable, intent(out) :: error integer :: stat - character(len=:), allocatable :: cmd_output, archive_format + character(len=:), allocatable :: cmd_output, archive_format, cmd + + if (verbose) then + print *, '' + print *, 'Show git archive options:' + print *, ' + git archive -l' + end if call execute_and_read_output('git archive -l', cmd_output, error) if (allocated(error)) return + if (verbose) print *, ' ', cmd_output + if (index(cmd_output, 'tar.gz') /= 0) then archive_format = 'tar.gz' else call fatal_error(error, "Cannot find a suitable archive format for 'git archive'."); return end if - call execute_command_line('git archive HEAD --format='//archive_format//' -o '//destination, exitstat=stat) + cmd = 'git archive '//ref//' --format='//archive_format//' -o '//destination + + if (verbose) then + print *, '' + print *, 'Archive ', ref, ' using ', archive_format, ':' + print *, ' + ', cmd + print *, '' + end if + + call execute_command_line(cmd, exitstat=stat) if (stat /= 0) then call fatal_error(error, "Error packing '"//source//"'."); return end if end - end module fpm_git From c983e484ff059b6076d20c04709f93c276ff5de6 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 15 Jun 2023 15:14:47 +0700 Subject: [PATCH 596/799] Add verbose printout to package upload --- src/fpm/cmd/publish.f90 | 2 +- src/fpm/downloader.f90 | 18 ++++++++++++++---- src/fpm/git.f90 | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/fpm/cmd/publish.f90 b/src/fpm/cmd/publish.f90 index 121316e7b4..43636c0e30 100644 --- a/src/fpm/cmd/publish.f90 +++ b/src/fpm/cmd/publish.f90 @@ -101,7 +101,7 @@ subroutine cmd_publish(settings) print *, 'Dry run successful. Generated tarball: ', tmp_file; return end if - call downloader%upload_form(official_registry_base_url//'/packages', upload_data, error) + call downloader%upload_form(official_registry_base_url//'/packages', upload_data, settings%verbose, error) call delete_file(tmp_file) if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) end diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index 7c5046df4e..b557d3ded6 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -76,23 +76,30 @@ subroutine get_file(url, tmp_pkg_file, error) end !> Perform an http post request with form data. - subroutine upload_form(endpoint, form_data, error) + subroutine upload_form(endpoint, form_data, verbose, error) + !> Endpoint to upload to. character(len=*), intent(in) :: endpoint + !> Form data to upload. type(string_t), intent(in) :: form_data(:) + !> Print additional information when true. + logical, intent(in) :: verbose + !> Error handling. type(error_t), allocatable, intent(out) :: error integer :: stat, i - character(len=:), allocatable :: form_data_str + character(len=:), allocatable :: form_data_str, cmd form_data_str = '' do i = 1, size(form_data) form_data_str = form_data_str//"-F '"//form_data(i)%s//"' " end do + cmd = 'curl -X POST -H "Content-Type: multipart/form-data" '//form_data_str//endpoint + if (which('curl') /= '') then print *, 'Uploading package ...' - call execute_command_line('curl -X POST -H "Content-Type: multipart/form-data" ' & - & //form_data_str//endpoint, exitstat=stat) + if (verbose) print *, ' + ', cmd + call execute_command_line(cmd, exitstat=stat) else call fatal_error(error, "'curl' not installed."); return end if @@ -104,8 +111,11 @@ subroutine upload_form(endpoint, form_data, error) !> Unpack a tarball to a destination. subroutine unpack(tmp_pkg_file, destination, error) + !> Path to tarball. character(*), intent(in) :: tmp_pkg_file + !> Destination to unpack to. character(*), intent(in) :: destination + !> Error handling. type(error_t), allocatable, intent(out) :: error integer :: stat diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index f8238b2075..b053427583 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -315,7 +315,7 @@ subroutine git_archive(source, destination, ref, verbose, error) character(*), intent(in) :: destination !> (Symbolic) Reference to be archived. character(*), intent(in) :: ref - !> Whether to print verbose output. + !> Print additional information when true. logical, intent(in) :: verbose !> Error handling. type(error_t), allocatable, intent(out) :: error From ea7b0c7f660502ddb1a8b14213de57a8efbaa841 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 15 Jun 2023 23:48:15 +0700 Subject: [PATCH 597/799] Use deferred type parameter --- src/fpm_os.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_os.F90 b/src/fpm_os.F90 index f0e2f5437e..9de85e7584 100644 --- a/src/fpm_os.F90 +++ b/src/fpm_os.F90 @@ -246,7 +246,7 @@ subroutine get_absolute_path_by_cd(path, absolute_path, error) !> Converts a path to an absolute, canonical path. subroutine convert_to_absolute_path(path, error) - character(len=*), intent(inout) :: path + character(len=:), allocatable, intent(inout) :: path type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: absolute_path From faeba620a9eb4e98e89c32c890b5aecf90726352 Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Fri, 16 Jun 2023 08:29:13 -0400 Subject: [PATCH 598/799] resolution for : Replace fixed-size character length of I/O lines to allocatable arrays #902 Remove fixed-length I/O by using the already-available getline(3f) procedure, which can read an arbitrary-length input line. This should resolve #902. --- src/fpm_filesystem.F90 | 39 ++++++++++++------------ src/fpm_strings.f90 | 69 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index d5637357d1..8da411ccf2 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -6,7 +6,7 @@ module fpm_filesystem OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use fpm_environment, only: separator, get_env, os_is_unix - use fpm_strings, only: f_string, replace, string_t, split, notabs, str_begins_with_str + use fpm_strings, only: f_string, replace, string_t, split, dilate, str_begins_with_str use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer use fpm_error, only : fpm_stop, error_t, fatal_error implicit none @@ -14,9 +14,8 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, execute_and_read_output, & + os_delete_dir, is_absolute_path, env_variable, get_home, execute_and_read_output, & get_dos_path - integer, parameter :: LINE_BUFFER_LEN = 32768 #ifndef FPM_BOOTSTRAP interface @@ -332,14 +331,13 @@ function read_lines_expanded(fh) result(lines) type(string_t), allocatable :: lines(:) integer :: i - integer :: ilen - character(LINE_BUFFER_LEN) :: line_buffer_read, line_buffer_expanded + integer :: iostat + character(len=:),allocatable :: line_buffer_read allocate(lines(number_of_rows(fh))) do i = 1, size(lines) - read(fh, '(A)') line_buffer_read - call notabs(line_buffer_read, line_buffer_expanded, ilen) - lines(i)%s = trim(line_buffer_expanded) + call getline(fh, line_buffer_read, iostat) + lines(i)%s = dilate(line_buffer_read) end do end function read_lines_expanded @@ -350,12 +348,11 @@ function read_lines(fh) result(lines) type(string_t), allocatable :: lines(:) integer :: i - character(LINE_BUFFER_LEN) :: line_buffer + integer :: iostat allocate(lines(number_of_rows(fh))) do i = 1, size(lines) - read(fh, '(A)') line_buffer - lines(i)%s = trim(line_buffer) + call getline(fh, lines(i)%s, iostat) end do end function read_lines @@ -560,6 +557,7 @@ logical function exists(filename) result(r) function get_temp_filename() result(tempfile) ! use iso_c_binding, only: c_ptr, C_NULL_PTR, c_f_pointer + integer, parameter :: MAX_FILENAME_LENGTH = 32768 character(:), allocatable :: tempfile type(c_ptr) :: c_tempfile_ptr @@ -582,7 +580,7 @@ end subroutine c_free end interface c_tempfile_ptr = c_tempnam(C_NULL_PTR, C_NULL_PTR) - call c_f_pointer(c_tempfile_ptr,c_tempfile,[LINE_BUFFER_LEN]) + call c_f_pointer(c_tempfile_ptr,c_tempfile,[MAX_FILENAME_LENGTH]) tempfile = f_string(c_tempfile) @@ -644,8 +642,9 @@ subroutine getline(unit, line, iostat, iomsg) !> Error message character(len=:), allocatable, optional :: iomsg - character(len=LINE_BUFFER_LEN) :: buffer - character(len=LINE_BUFFER_LEN) :: msg + integer, parameter :: FILENAME_MAX = 4096 + character(len=FILENAME_MAX) :: buffer + character(len=FILENAME_MAX) :: msg integer :: size integer :: stat @@ -1095,7 +1094,7 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) integer :: cmdstat, unit, stat = 0 character(len=:), allocatable :: cmdmsg, tmp_file - character(len=1000) :: output_line + character(len=:),allocatable :: output_line tmp_file = get_temp_filename() @@ -1105,12 +1104,12 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) open(newunit=unit, file=tmp_file, action='read', status='old') output = '' do - read(unit, *, iostat=stat) output_line - if (stat /= 0) exit - output = output//trim(output_line)//' ' + call getline(unit, output_line, stat) + if (stat /= 0) exit + output = output//output_line//' ' end do - close(unit, status='delete') - end + close(unit, status='delete',iostat=stat) + end subroutine execute_and_read_output !> Ensure a windows path is converted to an 8.3 DOS path if it contains spaces function get_dos_path(path,error) diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index e478f4dba6..6779437845 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -23,7 +23,8 @@ !! - [[IS_FORTRAN_NAME]] determine whether a string is an acceptable Fortran entity name !! - [[TO_FORTRAN_NAME]] replace allowed special but unusuable characters in names with underscore !!### Whitespace -!! - [[NOTABS]] Expand tab characters assuming a tab space every eight characters +!! - [[NOTABS]] subroutine to expand tab characters assuming a tab space every eight characters +!! - [[DILATE]] function to expand tab characters assuming a tab space every eight characters !! - [[LEN_TRIM]] Determine total trimmed length of **STRING_T** array !!### Miscellaneous !! - [[FNV_1A]] Hash a **CHARACTER(*)** string of default kind or a **TYPE(STRING_T)** array @@ -43,7 +44,7 @@ module fpm_strings public :: to_fortran_name, is_fortran_name public :: string_array_contains, string_cat, len_trim, operator(.in.), fnv_1a public :: replace, resize, str, join, glob -public :: notabs, remove_newline_characters +public :: notabs, dilate, remove_newline_characters !> Module naming public :: is_valid_module_name, is_valid_module_prefix, & @@ -1015,7 +1016,7 @@ pure function to_fortran_name(string) result(res) res = replace(string, SPECIAL_CHARACTERS, '_') end function to_fortran_name -function is_fortran_name(line) result (lout) +elemental function is_fortran_name(line) result (lout) ! determine if a string is a valid Fortran name ignoring trailing spaces ! (but not leading spaces) character(len=*),parameter :: int='0123456789' @@ -1365,4 +1366,66 @@ elemental impure subroutine notabs(instr,outstr,ilen) end subroutine notabs +!>AUTHOR: John S. Urban +!!LICENSE: Public Domain +!> +!!##NAME +!! dilate(3f) - [M_strings:NONALPHA] expand tab characters +!! (LICENSE:PD) +!! +!!##SYNOPSIS +!! +!! function dilate(INSTR) result(OUTSTR) +!! +!! character(len=*),intent=(in) :: INSTR +!! character(len=:),allocatable :: OUTSTR +!! +!!##DESCRIPTION +!! dilate() converts tabs in INSTR to spaces in OUTSTR. It assumes a +!! tab is set every 8 characters. Trailing spaces are removed. +!! +!! In addition, trailing carriage returns and line feeds are removed +!! (they are usually a problem created by going to and from MSWindows). +!! +!!##OPTIONS +!! instr Input line to remove tabs from +!! +!!##RESULTS +!! outstr Output string with tabs expanded. +!! +!!##EXAMPLES +!! +!! Sample program: +!! +!! program demo_dilate +!! +!! use M_strings, only : dilate +!! implicit none +!! character(len=:),allocatable :: in +!! integer :: i +!! in=' this is my string ' +!! ! change spaces to tabs to make a sample input +!! do i=1,len(in) +!! if(in(i:i) == ' ')in(i:i)=char(9) +!! enddo +!! write(*,'(a)')in,dilate(in) +!! end program demo_dilate +!! +function dilate(instr) result(outstr) + + character(len=*), intent(in) :: instr ! input line to scan for tab characters + character(len=:), allocatable :: outstr ! tab-expanded version of INSTR produced + integer :: i + integer :: icount + integer :: lgth + icount = 0 + do i = 1, len(instr) + if (instr(i:i) == char(9)) icount = icount + 1 + end do + allocate (character(len=(len(instr) + 8*icount)) :: outstr) + call notabs(instr, outstr, lgth) + outstr = outstr(:lgth) + +end function dilate + end module fpm_strings From b7e56132876943bf157f98f85eb4781b57cea8a6 Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Fri, 16 Jun 2023 08:29:13 -0400 Subject: [PATCH 599/799] resolution for : Replace fixed-size character length of I/O lines to allocatable arrays #902 Remove fixed-length I/O by using the already-available getline(3f) procedure, which can read an arbitrary-length input line. This should resolve #902. --- src/fpm_filesystem.F90 | 39 ++++++++++++------------ src/fpm_strings.f90 | 69 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index d5637357d1..8da411ccf2 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -6,7 +6,7 @@ module fpm_filesystem OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use fpm_environment, only: separator, get_env, os_is_unix - use fpm_strings, only: f_string, replace, string_t, split, notabs, str_begins_with_str + use fpm_strings, only: f_string, replace, string_t, split, dilate, str_begins_with_str use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer use fpm_error, only : fpm_stop, error_t, fatal_error implicit none @@ -14,9 +14,8 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, execute_and_read_output, & + os_delete_dir, is_absolute_path, env_variable, get_home, execute_and_read_output, & get_dos_path - integer, parameter :: LINE_BUFFER_LEN = 32768 #ifndef FPM_BOOTSTRAP interface @@ -332,14 +331,13 @@ function read_lines_expanded(fh) result(lines) type(string_t), allocatable :: lines(:) integer :: i - integer :: ilen - character(LINE_BUFFER_LEN) :: line_buffer_read, line_buffer_expanded + integer :: iostat + character(len=:),allocatable :: line_buffer_read allocate(lines(number_of_rows(fh))) do i = 1, size(lines) - read(fh, '(A)') line_buffer_read - call notabs(line_buffer_read, line_buffer_expanded, ilen) - lines(i)%s = trim(line_buffer_expanded) + call getline(fh, line_buffer_read, iostat) + lines(i)%s = dilate(line_buffer_read) end do end function read_lines_expanded @@ -350,12 +348,11 @@ function read_lines(fh) result(lines) type(string_t), allocatable :: lines(:) integer :: i - character(LINE_BUFFER_LEN) :: line_buffer + integer :: iostat allocate(lines(number_of_rows(fh))) do i = 1, size(lines) - read(fh, '(A)') line_buffer - lines(i)%s = trim(line_buffer) + call getline(fh, lines(i)%s, iostat) end do end function read_lines @@ -560,6 +557,7 @@ logical function exists(filename) result(r) function get_temp_filename() result(tempfile) ! use iso_c_binding, only: c_ptr, C_NULL_PTR, c_f_pointer + integer, parameter :: MAX_FILENAME_LENGTH = 32768 character(:), allocatable :: tempfile type(c_ptr) :: c_tempfile_ptr @@ -582,7 +580,7 @@ end subroutine c_free end interface c_tempfile_ptr = c_tempnam(C_NULL_PTR, C_NULL_PTR) - call c_f_pointer(c_tempfile_ptr,c_tempfile,[LINE_BUFFER_LEN]) + call c_f_pointer(c_tempfile_ptr,c_tempfile,[MAX_FILENAME_LENGTH]) tempfile = f_string(c_tempfile) @@ -644,8 +642,9 @@ subroutine getline(unit, line, iostat, iomsg) !> Error message character(len=:), allocatable, optional :: iomsg - character(len=LINE_BUFFER_LEN) :: buffer - character(len=LINE_BUFFER_LEN) :: msg + integer, parameter :: FILENAME_MAX = 4096 + character(len=FILENAME_MAX) :: buffer + character(len=FILENAME_MAX) :: msg integer :: size integer :: stat @@ -1095,7 +1094,7 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) integer :: cmdstat, unit, stat = 0 character(len=:), allocatable :: cmdmsg, tmp_file - character(len=1000) :: output_line + character(len=:),allocatable :: output_line tmp_file = get_temp_filename() @@ -1105,12 +1104,12 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) open(newunit=unit, file=tmp_file, action='read', status='old') output = '' do - read(unit, *, iostat=stat) output_line - if (stat /= 0) exit - output = output//trim(output_line)//' ' + call getline(unit, output_line, stat) + if (stat /= 0) exit + output = output//output_line//' ' end do - close(unit, status='delete') - end + close(unit, status='delete',iostat=stat) + end subroutine execute_and_read_output !> Ensure a windows path is converted to an 8.3 DOS path if it contains spaces function get_dos_path(path,error) diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index e478f4dba6..6779437845 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -23,7 +23,8 @@ !! - [[IS_FORTRAN_NAME]] determine whether a string is an acceptable Fortran entity name !! - [[TO_FORTRAN_NAME]] replace allowed special but unusuable characters in names with underscore !!### Whitespace -!! - [[NOTABS]] Expand tab characters assuming a tab space every eight characters +!! - [[NOTABS]] subroutine to expand tab characters assuming a tab space every eight characters +!! - [[DILATE]] function to expand tab characters assuming a tab space every eight characters !! - [[LEN_TRIM]] Determine total trimmed length of **STRING_T** array !!### Miscellaneous !! - [[FNV_1A]] Hash a **CHARACTER(*)** string of default kind or a **TYPE(STRING_T)** array @@ -43,7 +44,7 @@ module fpm_strings public :: to_fortran_name, is_fortran_name public :: string_array_contains, string_cat, len_trim, operator(.in.), fnv_1a public :: replace, resize, str, join, glob -public :: notabs, remove_newline_characters +public :: notabs, dilate, remove_newline_characters !> Module naming public :: is_valid_module_name, is_valid_module_prefix, & @@ -1015,7 +1016,7 @@ pure function to_fortran_name(string) result(res) res = replace(string, SPECIAL_CHARACTERS, '_') end function to_fortran_name -function is_fortran_name(line) result (lout) +elemental function is_fortran_name(line) result (lout) ! determine if a string is a valid Fortran name ignoring trailing spaces ! (but not leading spaces) character(len=*),parameter :: int='0123456789' @@ -1365,4 +1366,66 @@ elemental impure subroutine notabs(instr,outstr,ilen) end subroutine notabs +!>AUTHOR: John S. Urban +!!LICENSE: Public Domain +!> +!!##NAME +!! dilate(3f) - [M_strings:NONALPHA] expand tab characters +!! (LICENSE:PD) +!! +!!##SYNOPSIS +!! +!! function dilate(INSTR) result(OUTSTR) +!! +!! character(len=*),intent=(in) :: INSTR +!! character(len=:),allocatable :: OUTSTR +!! +!!##DESCRIPTION +!! dilate() converts tabs in INSTR to spaces in OUTSTR. It assumes a +!! tab is set every 8 characters. Trailing spaces are removed. +!! +!! In addition, trailing carriage returns and line feeds are removed +!! (they are usually a problem created by going to and from MSWindows). +!! +!!##OPTIONS +!! instr Input line to remove tabs from +!! +!!##RESULTS +!! outstr Output string with tabs expanded. +!! +!!##EXAMPLES +!! +!! Sample program: +!! +!! program demo_dilate +!! +!! use M_strings, only : dilate +!! implicit none +!! character(len=:),allocatable :: in +!! integer :: i +!! in=' this is my string ' +!! ! change spaces to tabs to make a sample input +!! do i=1,len(in) +!! if(in(i:i) == ' ')in(i:i)=char(9) +!! enddo +!! write(*,'(a)')in,dilate(in) +!! end program demo_dilate +!! +function dilate(instr) result(outstr) + + character(len=*), intent(in) :: instr ! input line to scan for tab characters + character(len=:), allocatable :: outstr ! tab-expanded version of INSTR produced + integer :: i + integer :: icount + integer :: lgth + icount = 0 + do i = 1, len(instr) + if (instr(i:i) == char(9)) icount = icount + 1 + end do + allocate (character(len=(len(instr) + 8*icount)) :: outstr) + call notabs(instr, outstr, lgth) + outstr = outstr(:lgth) + +end function dilate + end module fpm_strings From bac6f602a650ff7902a96c27aab8b071e832acda Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Fri, 16 Jun 2023 16:25:08 -0400 Subject: [PATCH 600/799] Remove ENV_VARIABLE() as it duplicates the functionality of GET_ENV() The ENV_VARIABLE() procedure is performing functions already available in the GET_ENV() procedure. This changes the ENV_VARIABLE() calls to GET_ENV() calls to eliminate the duplication functionality. --- src/fpm_compiler.F90 | 1 - src/fpm_filesystem.F90 | 41 +++++++++-------------------------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/src/fpm_compiler.F90 b/src/fpm_compiler.F90 index 2ba571cda5..02b99af135 100644 --- a/src/fpm_compiler.F90 +++ b/src/fpm_compiler.F90 @@ -28,7 +28,6 @@ module fpm_compiler use,intrinsic :: iso_fortran_env, only: stderr=>error_unit use fpm_environment, only: & - get_env, & get_os_type, & OS_LINUX, & OS_MACOS, & diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index d5637357d1..897063c6ff 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -14,7 +14,7 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, env_variable, get_home, execute_and_read_output, & + LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, get_home, execute_and_read_output, & get_dos_path integer, parameter :: LINE_BUFFER_LEN = 32768 @@ -54,29 +54,6 @@ end function c_is_dir contains - -!> return value of environment variable -subroutine env_variable(var, name) - character(len=:), allocatable, intent(out) :: var - character(len=*), intent(in) :: name - integer :: length, stat - - call get_environment_variable(name, length=length, status=stat) - if (stat /= 0) return - - allocate(character(len=length) :: var) - - if (length > 0) then - call get_environment_variable(name, var, status=stat) - if (stat /= 0) then - deallocate(var) - return - end if - end if - -end subroutine env_variable - - !> Extract filename from path with/without suffix function basename(path,suffix) result (base) @@ -1017,15 +994,15 @@ function get_local_prefix(os) result(prefix) character(len=:), allocatable :: home if (os_is_unix(os)) then - call env_variable(home, "HOME") - if (allocated(home)) then + home=get_env('HOME','') + if (home /= '' ) then prefix = join_path(home, ".local") else prefix = default_prefix_unix end if else - call env_variable(home, "APPDATA") - if (allocated(home)) then + home=get_env('APPDATA','') + if (home /= '' ) then prefix = join_path(home, "local") else prefix = default_prefix_win @@ -1068,14 +1045,14 @@ subroutine get_home(home, error) type(error_t), allocatable, intent(out) :: error if (os_is_unix()) then - call env_variable(home, 'HOME') - if (.not. allocated(home)) then + home=get_env('HOME','') + if ( home == '' ) then call fatal_error(error, "Couldn't retrieve 'HOME' variable") return end if else - call env_variable(home, 'USERPROFILE') - if (.not. allocated(home)) then + home=get_env('USERPROFILE','') + if ( home == '' ) then call fatal_error(error, "Couldn't retrieve '%USERPROFILE%' variable") return end if From f0337abb1ca6f82689a5c101047306babf01da73 Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Fri, 16 Jun 2023 16:42:28 -0400 Subject: [PATCH 601/799] change test_os.f90 accordingly --- test/fpm_test/test_os.f90 | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/fpm_test/test_os.f90 b/test/fpm_test/test_os.f90 index 594aa937a5..71989167f5 100644 --- a/test/fpm_test/test_os.f90 +++ b/test/fpm_test/test_os.f90 @@ -1,7 +1,7 @@ module test_os use testsuite, only: new_unittest, unittest_t, error_t, test_failed - use fpm_filesystem, only: env_variable, join_path, mkdir, os_delete_dir, is_dir, get_local_prefix, get_home - use fpm_environment, only: os_is_unix + use fpm_filesystem, only: join_path, mkdir, os_delete_dir, is_dir, get_local_prefix, get_home + use fpm_environment, only: os_is_unix, get_env use fpm_os, only: get_absolute_path, get_absolute_path_by_cd, get_current_directory implicit none @@ -134,7 +134,7 @@ subroutine abs_path_nonexisting(error) subroutine abs_path_root(error) type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: home_drive, home_path, result + character(len=:), allocatable :: home_path, result if (os_is_unix()) then call get_absolute_path('/', result, error) @@ -144,8 +144,7 @@ subroutine abs_path_root(error) call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'"); return end if else - call env_variable(home_drive, 'HOMEDRIVE') - home_path = home_drive//'\' + home_path = get_env('HOMEDRIVE','') //'\' call get_absolute_path(home_path, result, error) if (allocated(error)) return @@ -177,7 +176,7 @@ subroutine abs_path_home(error) subroutine abs_path_cd_root(error) type(error_t), allocatable, intent(out) :: error - character(len=:), allocatable :: home_drive, home_path, current_dir_before, current_dir_after, result + character(len=:), allocatable :: home_path, current_dir_before, current_dir_after, result call get_current_directory(current_dir_before, error) if (allocated(error)) return @@ -189,8 +188,7 @@ subroutine abs_path_cd_root(error) call test_failed(error, "Result '"//result//"' doesn't equal input value: '/'"); return end if else - call env_variable(home_drive, 'HOMEDRIVE') - home_path = home_drive//'\' + home_path = get_env('HOMEDRIVE','')//'\' call get_absolute_path_by_cd(home_path, result, error) From 40b78880de10e7ebf843a4e6802a9ccfaaf864b1 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 17 Jun 2023 15:10:30 +0200 Subject: [PATCH 602/799] add `runner_args` to cli --- src/fpm_command_line.f90 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index f1ced79308..5f36382bb2 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -90,6 +90,7 @@ module fpm_command_line character(len=ibug),allocatable :: name(:) character(len=:),allocatable :: args character(len=:),allocatable :: runner + character(len=:),allocatable :: runner_args logical :: example end type @@ -141,7 +142,7 @@ module fpm_command_line & 'test', 'runner', 'install', 'update', 'list', 'help', 'version', 'publish' ] character(len=:), allocatable :: val_runner, val_compiler, val_flag, val_cflag, val_cxxflag, val_ldflag, & - val_profile + val_profile, val_runner_args ! '12345678901234567890123456789012345678901234567890123456789012345678901234567890',& character(len=80), parameter :: help_text_build_common(*) = [character(len=80) :: & @@ -268,7 +269,8 @@ subroutine get_command_line_settings(cmd_settings) run_args = & ' --target " "' // & ' --list F' // & - ' --runner " "' + ' --runner " "'// & + ' --runner-args " "' compiler_args = & ' --profile " "' // & @@ -322,7 +324,8 @@ subroutine get_command_line_settings(cmd_settings) archiver = sget('archiver') allocate(fpm_run_settings :: cmd_settings) val_runner=sget('runner') - if(specified('runner') .and. val_runner=='')val_runner='echo' + val_runner_args=sget('runner-args') + if (specified('runner') .and. val_runner=='')val_runner='echo' cmd_settings=fpm_run_settings(& & args=remaining,& & profile=val_profile,& @@ -340,6 +343,7 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.false.,& & name=names,& & runner=val_runner,& + & runner_args=val_runner_args,& & verbose=lget('verbose') ) case('build') @@ -571,6 +575,7 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_test_settings :: cmd_settings) val_runner=sget('runner') if(specified('runner') .and. val_runner=='')val_runner='echo' + val_runner_args=sget('runner-args') cmd_settings=fpm_test_settings(& & args=remaining, & & profile=val_profile, & @@ -588,6 +593,7 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.true., & & name=names, & & runner=val_runner, & + & runner_args=val_runner_args, & & verbose=lget('verbose') ) case('update') From 8485e9467742653e2b68bd409b33965a8d11ce1c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 17 Jun 2023 15:17:28 +0200 Subject: [PATCH 603/799] add runner_command function --- src/fpm_command_line.f90 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 5f36382bb2..b45a9c592a 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -92,6 +92,8 @@ module fpm_command_line character(len=:),allocatable :: runner character(len=:),allocatable :: runner_args logical :: example + contains + procedure :: runner_command end type type, extends(fpm_run_settings) :: fpm_test_settings @@ -1436,4 +1438,21 @@ function get_fpm_env(env, default) result(val) val = get_env(fpm_prefix//env, default) end function get_fpm_env + !> Build a full runner command (executable + command-line arguments) + function runner_command(cmd) result(run_cmd) + class(fpm_run_settings), intent(in) :: cmd + character(len=:), allocatable :: run_cmd + + !> Get executable + if (len_trim(cmd%runner)>0) then + run_cmd = trim(cmd%runner) + else + run_cmd = '' + end if + + !> Append command-line arguments + if (len_trim(cmd%runner_args)>0) run_cmd = run_cmd//' '//trim(cmd%runner_args) + + end function runner_command + end module fpm_command_line From 4a515ebb124ee6a85d6c3cf5968d8f208fe59e1b Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 17 Jun 2023 15:23:35 +0200 Subject: [PATCH 604/799] Revert "add `runner_args` to cli" This reverts commit 40b78880de10e7ebf843a4e6802a9ccfaaf864b1. --- src/fpm_command_line.f90 | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index b45a9c592a..2c75ef10a4 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -90,7 +90,6 @@ module fpm_command_line character(len=ibug),allocatable :: name(:) character(len=:),allocatable :: args character(len=:),allocatable :: runner - character(len=:),allocatable :: runner_args logical :: example contains procedure :: runner_command @@ -144,7 +143,7 @@ module fpm_command_line & 'test', 'runner', 'install', 'update', 'list', 'help', 'version', 'publish' ] character(len=:), allocatable :: val_runner, val_compiler, val_flag, val_cflag, val_cxxflag, val_ldflag, & - val_profile, val_runner_args + val_profile ! '12345678901234567890123456789012345678901234567890123456789012345678901234567890',& character(len=80), parameter :: help_text_build_common(*) = [character(len=80) :: & @@ -271,8 +270,7 @@ subroutine get_command_line_settings(cmd_settings) run_args = & ' --target " "' // & ' --list F' // & - ' --runner " "'// & - ' --runner-args " "' + ' --runner " "' compiler_args = & ' --profile " "' // & @@ -326,8 +324,7 @@ subroutine get_command_line_settings(cmd_settings) archiver = sget('archiver') allocate(fpm_run_settings :: cmd_settings) val_runner=sget('runner') - val_runner_args=sget('runner-args') - if (specified('runner') .and. val_runner=='')val_runner='echo' + if(specified('runner') .and. val_runner=='')val_runner='echo' cmd_settings=fpm_run_settings(& & args=remaining,& & profile=val_profile,& @@ -345,7 +342,6 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.false.,& & name=names,& & runner=val_runner,& - & runner_args=val_runner_args,& & verbose=lget('verbose') ) case('build') @@ -577,7 +573,6 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_test_settings :: cmd_settings) val_runner=sget('runner') if(specified('runner') .and. val_runner=='')val_runner='echo' - val_runner_args=sget('runner-args') cmd_settings=fpm_test_settings(& & args=remaining, & & profile=val_profile, & @@ -595,7 +590,6 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.true., & & name=names, & & runner=val_runner, & - & runner_args=val_runner_args, & & verbose=lget('verbose') ) case('update') From 413073f0bcb7aac0f32b9a72efebc21105e4560c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 17 Jun 2023 15:23:39 +0200 Subject: [PATCH 605/799] Revert "add runner_command function" This reverts commit 8485e9467742653e2b68bd409b33965a8d11ce1c. --- src/fpm_command_line.f90 | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 2c75ef10a4..f1ced79308 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -91,8 +91,6 @@ module fpm_command_line character(len=:),allocatable :: args character(len=:),allocatable :: runner logical :: example - contains - procedure :: runner_command end type type, extends(fpm_run_settings) :: fpm_test_settings @@ -1432,21 +1430,4 @@ function get_fpm_env(env, default) result(val) val = get_env(fpm_prefix//env, default) end function get_fpm_env - !> Build a full runner command (executable + command-line arguments) - function runner_command(cmd) result(run_cmd) - class(fpm_run_settings), intent(in) :: cmd - character(len=:), allocatable :: run_cmd - - !> Get executable - if (len_trim(cmd%runner)>0) then - run_cmd = trim(cmd%runner) - else - run_cmd = '' - end if - - !> Append command-line arguments - if (len_trim(cmd%runner_args)>0) run_cmd = run_cmd//' '//trim(cmd%runner_args) - - end function runner_command - end module fpm_command_line From 86e3d18221890a48963c09c7189708213ec08a6c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 17 Jun 2023 16:00:28 +0200 Subject: [PATCH 606/799] add `runner-args` option --- src/fpm.f90 | 6 +++-- src/fpm_command_line.f90 | 48 ++++++++++++++++++++++++++++++++----- src/fpm_meta.f90 | 7 ++---- src/fpm_strings.f90 | 51 ++++++++++++++++++++++++++-------------- 4 files changed, 81 insertions(+), 31 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index e8ad5f255f..3fee264618 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -543,6 +543,8 @@ subroutine cmd_run(settings,test) end if end if + + ! Check all names are valid ! or no name and found more than one file toomany= size(settings%name)==0 .and. size(executables)>1 @@ -587,10 +589,10 @@ subroutine cmd_run(settings,test) if (exists(executables(i)%s)) then if(settings%runner /= ' ')then if(.not.allocated(settings%args))then - call run(settings%runner//' '//executables(i)%s, & + call run(settings%runner_command()//' '//executables(i)%s, & echo=settings%verbose, exitstat=stat(i)) else - call run(settings%runner//' '//executables(i)%s//" "//settings%args, & + call run(settings%runner_command()//' '//executables(i)%s//" "//settings%args, & echo=settings%verbose, exitstat=stat(i)) endif else diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index f1ced79308..552320cd22 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -28,7 +28,7 @@ module fpm_command_line OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified use M_CLI2, only : get_subcommand, CLI_RESPONSE_FILE -use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name +use fpm_strings, only : lower, split, to_fortran_name, is_fortran_name, remove_characters_in_set, string_t use fpm_filesystem, only : basename, canon_path, which, run use fpm_environment, only : get_command_arguments_quoted use fpm_error, only : fpm_stop, error_t @@ -88,9 +88,12 @@ module fpm_command_line type, extends(fpm_build_settings) :: fpm_run_settings character(len=ibug),allocatable :: name(:) - character(len=:),allocatable :: args + character(len=:),allocatable :: args ! passed to the app character(len=:),allocatable :: runner + character(len=:),allocatable :: runner_args ! passed to the runner logical :: example + contains + procedure :: runner_command end type type, extends(fpm_run_settings) :: fpm_test_settings @@ -141,7 +144,7 @@ module fpm_command_line & 'test', 'runner', 'install', 'update', 'list', 'help', 'version', 'publish' ] character(len=:), allocatable :: val_runner, val_compiler, val_flag, val_cflag, val_cxxflag, val_ldflag, & - val_profile + val_profile, val_runner_args ! '12345678901234567890123456789012345678901234567890123456789012345678901234567890',& character(len=80), parameter :: help_text_build_common(*) = [character(len=80) :: & @@ -268,7 +271,8 @@ subroutine get_command_line_settings(cmd_settings) run_args = & ' --target " "' // & ' --list F' // & - ' --runner " "' + ' --runner " "' // & + ' --runner-args " "' compiler_args = & ' --profile " "' // & @@ -317,12 +321,17 @@ subroutine get_command_line_settings(cmd_settings) if(names(i)=='..')names(i)='*' enddo + ! If there are additional command-line arguments, remove the additional + ! double quotes which have been added by M_CLI2 + call remove_characters_in_set(remaining,set='"') + c_compiler = sget('c-compiler') cxx_compiler = sget('cxx-compiler') archiver = sget('archiver') allocate(fpm_run_settings :: cmd_settings) val_runner=sget('runner') if(specified('runner') .and. val_runner=='')val_runner='echo' + val_runner_args=sget('runner-args') cmd_settings=fpm_run_settings(& & args=remaining,& & profile=val_profile,& @@ -340,6 +349,7 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.false.,& & name=names,& & runner=val_runner,& + & runner_args=val_runner_args, & & verbose=lget('verbose') ) case('build') @@ -565,12 +575,17 @@ subroutine get_command_line_settings(cmd_settings) if(names(i)=='..')names(i)='*' enddo + ! If there are additional command-line arguments, remove the additional + ! double quotes which have been added by M_CLI2 + call remove_characters_in_set(remaining,set='"') + c_compiler = sget('c-compiler') cxx_compiler = sget('cxx-compiler') archiver = sget('archiver') allocate(fpm_test_settings :: cmd_settings) val_runner=sget('runner') if(specified('runner') .and. val_runner=='')val_runner='echo' + val_runner_args=sget('runner-args') cmd_settings=fpm_test_settings(& & args=remaining, & & profile=val_profile, & @@ -588,6 +603,7 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.true., & & name=names, & & runner=val_runner, & + & runner_args=val_runner_args, & & verbose=lget('verbose') ) case('update') @@ -768,7 +784,7 @@ subroutine set_help() ' executables. ', & ' ', & 'SYNOPSIS ', & - ' fpm run|test --runner CMD ... -- SUFFIX_OPTIONS ', & + ' fpm run|test --runner CMD ... --runner-args ARGS -- SUFFIX_OPTIONS ', & ' ', & 'DESCRIPTION ', & ' The --runner option allows specifying a program to launch ', & @@ -784,8 +800,11 @@ subroutine set_help() ' Available for both the "run" and "test" subcommands. ', & ' If the keyword is specified without a value the default command ', & ' is "echo". ', & + ' --runner-args "args" an additional option to pass command-line arguments ', & + ' to the runner command, instead of to the fpm app. ', & ' -- SUFFIX_OPTIONS additional options to suffix the command CMD and executable ', & - ' file names with. ', & + ' file names with. These options are passed as command-line ', & + ' arguments to the app. ', & 'EXAMPLES ', & ' Use cases for ''fpm run|test --runner "CMD"'' include employing ', & ' the following common GNU/Linux and Unix commands: ', & @@ -814,6 +833,7 @@ subroutine set_help() ' ', & ' fpm test --runner gdb ', & ' fpm run --runner "tar cvfz $HOME/bundle.tgz" ', & + ' fpm run --runner "mpiexec" --runner-args "-np 12" ', & ' fpm run --runner ldd ', & ' fpm run --runner strip ', & ' fpm run --runner ''cp -t /usr/local/bin'' ', & @@ -1430,4 +1450,20 @@ function get_fpm_env(env, default) result(val) val = get_env(fpm_prefix//env, default) end function get_fpm_env + + !> Build a full runner command (executable + command-line arguments) + function runner_command(cmd) result(run_cmd) + class(fpm_run_settings), intent(in) :: cmd + character(len=:), allocatable :: run_cmd + !> Get executable + if (len_trim(cmd%runner)>0) then + run_cmd = trim(cmd%runner) + else + run_cmd = '' + end if + !> Append command-line arguments + if (len_trim(cmd%runner_args)>0) run_cmd = run_cmd//' '//trim(cmd%runner_args) + end function runner_command + + end module fpm_command_line diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 9df0c6c2bb..9c5997f1aa 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -289,11 +289,8 @@ subroutine resolve_cmd(self,settings,error) select type (cmd=>settings) class is (fpm_run_settings) ! includes fpm_test_settings - if (.not.allocated(cmd%runner)) then - cmd%runner = self%run_command%s - else - cmd%runner = self%run_command%s//' '//cmd%runner - end if + ! Only override runner if user has not provided a custom one + if (.not.len_trim(cmd%runner)>0) cmd%runner = self%run_command%s end select diff --git a/src/fpm_strings.f90 b/src/fpm_strings.f90 index e478f4dba6..f71b23a1b1 100644 --- a/src/fpm_strings.f90 +++ b/src/fpm_strings.f90 @@ -43,7 +43,7 @@ module fpm_strings public :: to_fortran_name, is_fortran_name public :: string_array_contains, string_cat, len_trim, operator(.in.), fnv_1a public :: replace, resize, str, join, glob -public :: notabs, remove_newline_characters +public :: notabs, remove_newline_characters, remove_characters_in_set !> Module naming public :: is_valid_module_name, is_valid_module_prefix, & @@ -1220,44 +1220,59 @@ logical function has_valid_standard_prefix(module_name,package_name) result(vali end function has_valid_standard_prefix -! Remove all new line characters from the current string, replace them with spaces -subroutine remove_newline_characters(string) - type(string_t), intent(inout) :: string +! Remove all characters from a set from a string +subroutine remove_characters_in_set(string,set,replace_with) + character(len=:), allocatable, intent(inout) :: string + character(*), intent(in) :: set + character, optional, intent(in) :: replace_with ! Replace with this character instead of removing integer :: feed,length - character(*), parameter :: CRLF = new_line('a')//achar(13) - character(*), parameter :: SPACE = ' ' + if (.not.allocated(string)) return + if (len(set)<=0) return - if (.not.allocated(string%s)) return - - - length = len(string%s) - feed = scan(string%s,CRLF) + length = len(string) + feed = scan(string,set) do while (length>0 .and. feed>0) ! Remove heading if (length==1) then - string = string_t("") + string = "" elseif (feed==1) then - string%s = string%s(2:length) + string = string(2:length) ! Remove trailing elseif (feed==length) then - string%s = string%s(1:length-1) + string = string(1:length-1) - ! In between: replace with space + ! In between: replace with given character + elseif (present(replace_with)) then + string(feed:feed) = replace_with + ! Or just remove else - string%s(feed:feed) = SPACE + string = string(1:feed-1)//string(feed+1:length) end if - length = len(string%s) - feed = scan(string%s,CRLF) + length = len(string) + feed = scan(string,set) end do +end subroutine remove_characters_in_set + +! Remove all new line characters from the current string, replace them with spaces +subroutine remove_newline_characters(string) + type(string_t), intent(inout) :: string + + integer :: feed,length + + character(*), parameter :: CRLF = new_line('a')//achar(13) + character(*), parameter :: SPACE = ' ' + + call remove_characters_in_set(string%s,set=CRLF,replace_with=SPACE) + end subroutine remove_newline_characters !>AUTHOR: John S. Urban From d3c3a16087f6224051f6fdecbb4fd4a0a2763ae3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sat, 17 Jun 2023 16:15:18 +0200 Subject: [PATCH 607/799] fix app args --- src/fpm_command_line.f90 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 552320cd22..b895d5f6dc 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -323,7 +323,8 @@ subroutine get_command_line_settings(cmd_settings) ! If there are additional command-line arguments, remove the additional ! double quotes which have been added by M_CLI2 - call remove_characters_in_set(remaining,set='"') + val_runner_args=sget('runner-args') + call remove_characters_in_set(val_runner_args,set='"') c_compiler = sget('c-compiler') cxx_compiler = sget('cxx-compiler') @@ -331,7 +332,7 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_run_settings :: cmd_settings) val_runner=sget('runner') if(specified('runner') .and. val_runner=='')val_runner='echo' - val_runner_args=sget('runner-args') + cmd_settings=fpm_run_settings(& & args=remaining,& & profile=val_profile,& @@ -577,7 +578,8 @@ subroutine get_command_line_settings(cmd_settings) ! If there are additional command-line arguments, remove the additional ! double quotes which have been added by M_CLI2 - call remove_characters_in_set(remaining,set='"') + val_runner_args=sget('runner-args') + call remove_characters_in_set(val_runner_args,set='"') c_compiler = sget('c-compiler') cxx_compiler = sget('cxx-compiler') @@ -585,7 +587,7 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_test_settings :: cmd_settings) val_runner=sget('runner') if(specified('runner') .and. val_runner=='')val_runner='echo' - val_runner_args=sget('runner-args') + cmd_settings=fpm_test_settings(& & args=remaining, & & profile=val_profile, & From f3d2c1366d8604738711085cc28d5469ec771bf5 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 17 Jun 2023 22:30:00 +0700 Subject: [PATCH 608/799] Use run --- src/fpm/downloader.f90 | 12 +++++------- src/fpm/git.f90 | 26 +++++--------------------- src/fpm_filesystem.F90 | 20 ++++++++++++++------ 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index b557d3ded6..c481324fd4 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -1,6 +1,6 @@ module fpm_downloader use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only: which + use fpm_filesystem, only: which, run use fpm_versioning, only: version_t use jonquil, only: json_object, json_value, json_error, json_load, cast_to_object use fpm_strings, only: string_t @@ -81,25 +81,23 @@ subroutine upload_form(endpoint, form_data, verbose, error) character(len=*), intent(in) :: endpoint !> Form data to upload. type(string_t), intent(in) :: form_data(:) - !> Print additional information when true. + !> Print additional information if true. logical, intent(in) :: verbose !> Error handling. type(error_t), allocatable, intent(out) :: error integer :: stat, i - character(len=:), allocatable :: form_data_str, cmd + character(len=:), allocatable :: form_data_str form_data_str = '' do i = 1, size(form_data) form_data_str = form_data_str//"-F '"//form_data(i)%s//"' " end do - cmd = 'curl -X POST -H "Content-Type: multipart/form-data" '//form_data_str//endpoint - if (which('curl') /= '') then print *, 'Uploading package ...' - if (verbose) print *, ' + ', cmd - call execute_command_line(cmd, exitstat=stat) + call run('curl -X POST -H "Content-Type: multipart/form-data" '// & + & form_data_str//endpoint, exitstat=stat, verbose=verbose) else call fatal_error(error, "'curl' not installed."); return end if diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index b053427583..602c3c0439 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -1,7 +1,8 @@ !> Implementation for interacting with git repositories. module fpm_git use fpm_error, only: error_t, fatal_error - use fpm_filesystem, only : get_temp_filename, getline, join_path, execute_and_read_output + use fpm_filesystem, only : get_temp_filename, getline, join_path, execute_and_read_output, run + implicit none public :: git_target_t, git_target_default, git_target_branch, git_target_tag, git_target_revision, git_revision, & @@ -321,35 +322,18 @@ subroutine git_archive(source, destination, ref, verbose, error) type(error_t), allocatable, intent(out) :: error integer :: stat - character(len=:), allocatable :: cmd_output, archive_format, cmd - - if (verbose) then - print *, '' - print *, 'Show git archive options:' - print *, ' + git archive -l' - end if + character(len=:), allocatable :: cmd_output, archive_format - call execute_and_read_output('git archive -l', cmd_output, error) + call execute_and_read_output('git archive -l', cmd_output, error, verbose) if (allocated(error)) return - if (verbose) print *, ' ', cmd_output - if (index(cmd_output, 'tar.gz') /= 0) then archive_format = 'tar.gz' else call fatal_error(error, "Cannot find a suitable archive format for 'git archive'."); return end if - cmd = 'git archive '//ref//' --format='//archive_format//' -o '//destination - - if (verbose) then - print *, '' - print *, 'Archive ', ref, ' using ', archive_format, ':' - print *, ' + ', cmd - print *, '' - end if - - call execute_command_line(cmd, exitstat=stat) + call run('git archive '//ref//' --format='//archive_format//' -o '//destination, echo=verbose, exitstat=stat) if (stat /= 0) then call fatal_error(error, "Error packing '"//source//"'."); return end if diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index d5637357d1..7e77000a2f 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -1083,24 +1083,31 @@ subroutine get_home(home, error) end subroutine get_home !> Execute command line and return output as a string. - subroutine execute_and_read_output(cmd, output, error, exitstat) + subroutine execute_and_read_output(cmd, output, error, verbose) !> Command to execute. character(len=*), intent(in) :: cmd !> Command line output. character(len=:), allocatable, intent(out) :: output !> Error to handle. type(error_t), allocatable, intent(out) :: error - !> Can optionally used for error handling. - integer, intent(out), optional :: exitstat + !> Print additional information if true. + logical, intent(in), optional :: verbose - integer :: cmdstat, unit, stat = 0 + integer :: exitstat, unit, stat = 0 character(len=:), allocatable :: cmdmsg, tmp_file character(len=1000) :: output_line + logical :: is_verbose + + if (present(verbose)) then + is_verbose = verbose + else + is_verbose = .false. + end if tmp_file = get_temp_filename() - call execute_command_line(cmd//' > '//tmp_file, exitstat=exitstat, cmdstat=cmdstat) - if (cmdstat /= 0) call fatal_error(error, '*run*: '//"Command failed: '"//cmd//"'. Message: '"//trim(cmdmsg)//"'.") + call run(cmd//' > '//tmp_file, exitstat=exitstat, echo=is_verbose) + if (exitstat /= 0) call fatal_error(error, '*run*: '//"Command failed: '"//cmd//"'. Message: '"//trim(cmdmsg)//"'.") open(newunit=unit, file=tmp_file, action='read', status='old') output = '' @@ -1109,6 +1116,7 @@ subroutine execute_and_read_output(cmd, output, error, exitstat) if (stat /= 0) exit output = output//trim(output_line)//' ' end do + if (is_verbose) print *, output close(unit, status='delete') end From 21a71de61c6d4ceb2c4f16749839662a63889cdf Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sat, 17 Jun 2023 22:55:14 +0700 Subject: [PATCH 609/799] Do not initialize stat --- src/fpm_filesystem.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 45d748831d..b493b2e886 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -1152,7 +1152,7 @@ subroutine execute_and_read_output(cmd, output, error, verbose) !> Print additional information if true. logical, intent(in), optional :: verbose - integer :: exitstat, unit, stat = 0 + integer :: exitstat, unit, stat character(len=:), allocatable :: cmdmsg, tmp_file, output_line logical :: is_verbose @@ -1175,7 +1175,7 @@ subroutine execute_and_read_output(cmd, output, error, verbose) output = output//output_line//' ' end do if (is_verbose) print *, output - close(unit, status='delete', iostat=stat) + close(unit, status='delete') end !> Ensure a windows path is converted to an 8.3 DOS path if it contains spaces From 1b38b982c2a586eedc96b05f3e1cbe0e5ddbeae1 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 18 Jun 2023 09:45:57 +0700 Subject: [PATCH 610/799] Change verbose to echo --- src/fpm/downloader.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/downloader.f90 b/src/fpm/downloader.f90 index c481324fd4..39a3314ccf 100644 --- a/src/fpm/downloader.f90 +++ b/src/fpm/downloader.f90 @@ -97,7 +97,7 @@ subroutine upload_form(endpoint, form_data, verbose, error) if (which('curl') /= '') then print *, 'Uploading package ...' call run('curl -X POST -H "Content-Type: multipart/form-data" '// & - & form_data_str//endpoint, exitstat=stat, verbose=verbose) + & form_data_str//endpoint, exitstat=stat, echo=verbose) else call fatal_error(error, "'curl' not installed."); return end if From 953c57665d599d129564460ce354d29bfbe4b390 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 18 Jun 2023 10:01:07 +0700 Subject: [PATCH 611/799] Nit --- src/fpm/git.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm/git.f90 b/src/fpm/git.f90 index 602c3c0439..c007743a90 100644 --- a/src/fpm/git.f90 +++ b/src/fpm/git.f90 @@ -316,7 +316,7 @@ subroutine git_archive(source, destination, ref, verbose, error) character(*), intent(in) :: destination !> (Symbolic) Reference to be archived. character(*), intent(in) :: ref - !> Print additional information when true. + !> Print additional information if true. logical, intent(in) :: verbose !> Error handling. type(error_t), allocatable, intent(out) :: error From d69203ebca55688c57ed138d66c34d03a4f5adcd Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 18 Jun 2023 14:51:25 +0200 Subject: [PATCH 612/799] use clang as the brew compiler --- .github/workflows/meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 93075fd812..0423fe90f8 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -198,7 +198,7 @@ jobs: - name: (macOS) Install homebrew OpenMPI if: contains(matrix.mpi,'openmpi') && contains(matrix.os,'macos') run: | - brew install --cc=gcc-${{ env.GCC_V }} openmpi + brew install openmpi #--cc=gcc-${{ env.GCC_V }} openmpi # Phase 1: Bootstrap fpm with existing version - name: Install fpm From e279d452405440e4bc4b1d920777d9a4f1606436 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 18 Jun 2023 14:52:25 +0200 Subject: [PATCH 613/799] fix comment --- src/fpm_meta.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c8fd4171de..c610e5ad4f 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -8,6 +8,10 @@ !>### Available core libraries !> !> - OpenMP +!> - MPI +!> - fortran-lang stdlib +!> - fortran-lang minpack +!> !> !> @note Core libraries are enabled in the [build] section of the fpm.toml manifest !> From b937d646850054db2942e9f18e63a817c5f7eb4a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Mon, 19 Jun 2023 01:10:21 +0700 Subject: [PATCH 614/799] Refactor --- src/fpm.f90 | 35 +++++++++++++------------- src/fpm/cmd/update.f90 | 6 ++--- src/fpm/dependency.f90 | 56 ++++++++++++++++++++---------------------- src/fpm_filesystem.F90 | 4 ++- src/fpm_settings.f90 | 55 ++++++++++++++++++++--------------------- 5 files changed, 76 insertions(+), 80 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index e8ad5f255f..9e82b91d97 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -42,7 +42,6 @@ subroutine build_model(model, settings, package, error) integer :: i, j type(package_config_t) :: dependency character(len=:), allocatable :: manifest, lib_dir - character(len=:), allocatable :: version logical :: has_cpp logical :: duplicates_found type(string_t) :: include_dir @@ -324,7 +323,7 @@ end subroutine check_modules_for_duplicates subroutine check_module_names(model, error) type(fpm_model_t), intent(in) :: model type(error_t), allocatable, intent(out) :: error - integer :: i,j,k,l,m + integer :: k,l,m logical :: valid,errors_found,enforce_this_file type(string_t) :: package_name,module_name,package_prefix @@ -617,17 +616,19 @@ subroutine cmd_run(settings,test) call fpm_stop(stat(firsterror),'*cmd_run*:stopping due to failed executions') end if - endif + end if + contains + subroutine compact_list_all() integer, parameter :: LINE_WIDTH = 80 - integer :: i, j, nCol - j = 1 + integer :: ii, jj, nCol + jj = 1 nCol = LINE_WIDTH/col_width write(stderr,*) 'Available names:' - do i=1,size(targets) + do ii=1,size(targets) - exe_target => targets(i)%ptr + exe_target => targets(ii)%ptr if (exe_target%target_type == FPM_TARGET_EXECUTABLE .and. & allocated(exe_target%dependencies)) then @@ -635,11 +636,9 @@ subroutine compact_list_all() exe_source => exe_target%dependencies(1)%ptr%source if (exe_source%unit_scope == run_scope) then - - write(stderr,'(A)',advance=(merge("yes","no ",modulo(j,nCol)==0))) & + write(stderr,'(A)',advance=(merge("yes","no ",modulo(jj,nCol)==0))) & & [character(len=col_width) :: basename(exe_target%output_file, suffix=.false.)] - j = j + 1 - + jj = jj + 1 end if end if end do @@ -648,15 +647,15 @@ end subroutine compact_list_all subroutine compact_list() integer, parameter :: LINE_WIDTH = 80 - integer :: i, j, nCol - j = 1 + integer :: ii, jj, nCol + jj = 1 nCol = LINE_WIDTH/col_width write(stderr,*) 'Matched names:' - do i=1,size(executables) - write(stderr,'(A)',advance=(merge("yes","no ",modulo(j,nCol)==0))) & - & [character(len=col_width) :: basename(executables(i)%s, suffix=.false.)] - j = j + 1 - enddo + do ii=1,size(executables) + write(stderr,'(A)',advance=(merge("yes","no ",modulo(jj,nCol)==0))) & + & [character(len=col_width) :: basename(executables(ii)%s, suffix=.false.)] + jj = jj + 1 + end do write(stderr,*) end subroutine compact_list diff --git a/src/fpm/cmd/update.f90 b/src/fpm/cmd/update.f90 index e1bcb7326c..513e69599f 100644 --- a/src/fpm/cmd/update.f90 +++ b/src/fpm/cmd/update.f90 @@ -24,15 +24,13 @@ subroutine cmd_update(settings) call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) call handle_error(error) - if (.not.exists("build")) then + if (.not. exists("build")) then call mkdir("build") call filewrite(join_path("build", ".gitignore"),["*"]) end if cache = join_path("build", "cache.toml") - if (settings%clean) then - call delete_file(cache) - end if + if (settings%clean) call delete_file(cache) call new_dependency_tree(deps, cache=cache, & verbosity=merge(2, 1, settings%verbose)) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 600c43fdb2..af6860a0ac 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -123,7 +123,9 @@ module fpm_dependency type(dependency_node_t), allocatable :: dep(:) !> Cache file character(len=:), allocatable :: cache + contains + !> Overload procedure to add new dependencies to the tree generic :: add => add_project, add_project_dependencies, add_dependencies, & add_dependency, add_dependency_node @@ -194,13 +196,9 @@ subroutine new_dependency_tree(self, verbosity, cache) call resize(self%dep) self%dep_dir = join_path("build", "dependencies") - if (present(verbosity)) then - self%verbosity = verbosity - end if + if (present(verbosity)) self%verbosity = verbosity - if (present(cache)) then - self%cache = cache - end if + if (present(cache)) self%cache = cache end subroutine new_dependency_tree @@ -311,15 +309,15 @@ subroutine add_project(self, package, error) ! After resolving all dependencies, check if we have cached ones to avoid updates if (allocated(self%cache)) then - call new_dependency_tree(cached, verbosity=self%verbosity,cache=self%cache) + call new_dependency_tree(cached, verbosity=self%verbosity, cache=self%cache) call cached%load(self%cache, error) if (allocated(error)) return ! Skip root node - do id=2,cached%ndep - cached%dep(id)%cached = .true. - call self%add(cached%dep(id), error) - if (allocated(error)) return + do id = 2, cached%ndep + cached%dep(id)%cached = .true. + call self%add(cached%dep(id), error) + if (allocated(error)) return end do end if @@ -443,13 +441,13 @@ subroutine add_dependency_node(self, dependency, error) ! the manifest has priority if (dependency%cached) then if (dependency_has_changed(dependency, self%dep(id), self%verbosity, self%unit)) then - if (self%verbosity>0) write (self%unit, out_fmt) "Dependency change detected:", dependency%name - self%dep(id)%update = .true. + if (self%verbosity > 0) write (self%unit, out_fmt) "Dependency change detected:", dependency%name + self%dep(id)%update = .true. else - ! Store the cached one - self%dep(id) = dependency - self%dep(id)%update = .false. - endif + ! Store the cached one + self%dep(id) = dependency + self%dep(id)%update = .false. + end if end if else ! New dependency: add from scratch @@ -498,7 +496,7 @@ subroutine update_dependency(self, name, error) associate (dep => self%dep(id)) if (allocated(dep%git) .and. dep%update) then - if (self%verbosity>0) write (self%unit, out_fmt) "Update:", dep%name + if (self%verbosity > 0) write (self%unit, out_fmt) "Update:", dep%name proj_dir = join_path(self%dep_dir, dep%name) call dep%git%checkout(proj_dir, error) if (allocated(error)) return @@ -722,7 +720,7 @@ subroutine check_and_read_pkg_data(json, node, download_url, version, error) character(:), allocatable :: version_key, version_str, error_message, namespace, name namespace = "" - name = "UNNAMED_NODE" + name = "UNNAMED_NODE" if (allocated(node%namespace)) namespace = node%namespace if (allocated(node%name)) name = node%name @@ -1199,27 +1197,27 @@ logical function dependency_has_changed(cached, manifest, verbosity, iunit) resu !> may not have it if (allocated(cached%version) .and. allocated(manifest%version)) then if (cached%version /= manifest%version) then - if (verbosity>1) write(iunit,out_fmt) "VERSION has changed: "//cached%version%s()//" vs. "//manifest%version%s() - return - endif + if (verbosity > 1) write (iunit, out_fmt) "VERSION has changed: "//cached%version%s()//" vs. "//manifest%version%s() + return + end if else - if (verbosity>1) write(iunit,out_fmt) "VERSION has changed presence " + if (verbosity > 1) write (iunit, out_fmt) "VERSION has changed presence " end if if (allocated(cached%revision) .and. allocated(manifest%revision)) then if (cached%revision /= manifest%revision) then - if (verbosity>1) write(iunit,out_fmt) "REVISION has changed: "//cached%revision//" vs. "//manifest%revision + if (verbosity > 1) write (iunit, out_fmt) "REVISION has changed: "//cached%revision//" vs. "//manifest%revision return - endif + end if else - if (verbosity>1) write(iunit,out_fmt) "REVISION has changed presence " + if (verbosity > 1) write (iunit, out_fmt) "REVISION has changed presence " end if if (allocated(cached%proj_dir) .and. allocated(manifest%proj_dir)) then if (cached%proj_dir /= manifest%proj_dir) then - if (verbosity>1) write(iunit,out_fmt) "PROJECT DIR has changed: "//cached%proj_dir//" vs. "//manifest%proj_dir + if (verbosity > 1) write (iunit, out_fmt) "PROJECT DIR has changed: "//cached%proj_dir//" vs. "//manifest%proj_dir return - endif + end if else - if (verbosity>1) write(iunit,out_fmt) "PROJECT DIR has changed presence " + if (verbosity > 1) write (iunit, out_fmt) "PROJECT DIR has changed presence " end if !> All checks passed: the two dependencies have no differences diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 9b2112b18a..db0dde98e1 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -76,7 +76,9 @@ subroutine env_variable(var, name) end subroutine env_variable -!> Extract filename from path with/without suffix +!> Extract filename from path with or without suffix. +!> +!> The suffix is included by default. function basename(path,suffix) result (base) character(*), intent(In) :: path diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index 0e01ac5768..fe4b0748fa 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -4,13 +4,14 @@ module fpm_settings use fpm_environment, only: os_is_unix use fpm_error, only: error_t, fatal_error use fpm_toml, only: toml_table, toml_error, toml_stat, get_value, toml_load, check_keys - use fpm_os, only: get_current_directory, change_directory, get_absolute_path, & - convert_to_absolute_path + use fpm_os, only: get_current_directory, change_directory, get_absolute_path, convert_to_absolute_path + implicit none private public :: fpm_global_settings, get_global_settings, get_registry_settings, official_registry_base_url character(*), parameter :: official_registry_base_url = 'https://registry-apis.vercel.app' + character(*), parameter :: default_config_file_name = 'config.toml' type :: fpm_global_settings !> Path to the global config file excluding the file name. @@ -20,7 +21,7 @@ module fpm_settings !> Registry configs. type(fpm_registry_settings), allocatable :: registry_settings contains - procedure :: has_custom_location, full_path + procedure :: has_custom_location, full_path, path_to_config_folder_or_empty end type type :: fpm_registry_settings @@ -56,8 +57,8 @@ subroutine get_global_settings(global_settings, error) ! Use custom path to the config file if it was specified. if (global_settings%has_custom_location()) then ! Throw error if folder doesn't exist. - if (.not. exists(config_path(global_settings))) then - call fatal_error(error, "Folder not found: '"//config_path(global_settings)//"'."); return + if (.not. exists(global_settings%path_to_config_folder)) then + call fatal_error(error, "Folder not found: '"//global_settings%path_to_config_folder//"'."); return end if ! Throw error if the file doesn't exist. @@ -77,7 +78,7 @@ subroutine get_global_settings(global_settings, error) end if ! Use default file name. - global_settings%config_file_name = 'config.toml' + global_settings%config_file_name = default_config_file_name ! Apply default registry settings and return if config file doesn't exist. if (.not. exists(global_settings%full_path())) then @@ -105,8 +106,7 @@ subroutine get_global_settings(global_settings, error) else call use_default_registry_settings(global_settings) end if - - end subroutine get_global_settings + end !> Default registry settings are typically applied if the config file doesn't exist or no registry table was found in !> the global config file. @@ -115,9 +115,9 @@ subroutine use_default_registry_settings(global_settings) allocate (global_settings%registry_settings) global_settings%registry_settings%url = official_registry_base_url - global_settings%registry_settings%cache_path = join_path(config_path(global_settings), & + global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder_or_empty(), & & 'dependencies') - end subroutine use_default_registry_settings + end !> Read registry settings from the global config file. subroutine get_registry_settings(table, global_settings, error) @@ -155,7 +155,7 @@ subroutine get_registry_settings(table, global_settings, error) global_settings%registry_settings%path = path else ! Get canonical, absolute path on both Unix and Windows. - call get_absolute_path(join_path(config_path(global_settings), path), & + call get_absolute_path(join_path(global_settings%path_to_config_folder_or_empty(), path), & & global_settings%registry_settings%path, error) if (allocated(error)) return @@ -201,45 +201,44 @@ subroutine get_registry_settings(table, global_settings, error) if (.not. exists(cache_path)) call mkdir(cache_path) global_settings%registry_settings%cache_path = cache_path else - cache_path = join_path(config_path(global_settings), cache_path) + cache_path = join_path(global_settings%path_to_config_folder_or_empty(), cache_path) if (.not. exists(cache_path)) call mkdir(cache_path) ! Get canonical, absolute path on both Unix and Windows. call get_absolute_path(cache_path, global_settings%registry_settings%cache_path, error) if (allocated(error)) return end if else if (.not. allocated(path)) then - global_settings%registry_settings%cache_path = join_path(config_path(global_settings), & - & 'dependencies') + global_settings%registry_settings%cache_path = & + join_path(global_settings%path_to_config_folder_or_empty(), 'dependencies') end if - end subroutine get_registry_settings + end !> True if the global config file is not at the default location. - pure logical function has_custom_location(self) + elemental logical function has_custom_location(self) class(fpm_global_settings), intent(in) :: self has_custom_location = allocated(self%path_to_config_folder) .and. allocated(self%config_file_name) - if (.not.has_custom_location) return - has_custom_location = len_trim(self%path_to_config_folder)>0 .and. len_trim(self%config_file_name)>0 - end function + if (.not. has_custom_location) return + has_custom_location = len_trim(self%path_to_config_folder) > 0 .and. len_trim(self%config_file_name) > 0 + end !> The full path to the global config file. function full_path(self) result(result) class(fpm_global_settings), intent(in) :: self character(len=:), allocatable :: result - result = join_path(config_path(self), self%config_file_name) - end function + result = join_path(self%path_to_config_folder_or_empty(), self%config_file_name) + end !> The path to the global config directory. - function config_path(self) + pure function path_to_config_folder_or_empty(self) class(fpm_global_settings), intent(in) :: self - character(len=:), allocatable :: config_path + character(len=:), allocatable :: path_to_config_folder_or_empty if (allocated(self%path_to_config_folder)) then - config_path = self%path_to_config_folder + path_to_config_folder_or_empty = self%path_to_config_folder else - config_path = "" + path_to_config_folder_or_empty = "" end if - end function config_path - -end module fpm_settings + end +end From 702ee64655f5b1bf629857bbfbd7a5bfb2e0d0f7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 18 Jun 2023 22:07:06 +0200 Subject: [PATCH 615/799] try homebrew-no-auto-update --- .github/workflows/meta.yml | 2 +- src/fpm_meta.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 0423fe90f8..6370d6dc11 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -15,7 +15,7 @@ on: env: CI: "ON" # We can detect this in the build system and other vendors implement it HOMEBREW_NO_ANALYTICS: "ON" # Make Homebrew installation a little quicker - HOMEBREW_NO_AUTO_UPDATE: "ON" + HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" HOMEBREW_NO_GITHUB_API: "ON" HOMEBREW_NO_INSTALL_CLEANUP: "ON" diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index c610e5ad4f..432d17495f 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -3,7 +3,7 @@ !> This is a wrapper data type that encapsulate all pre-processing information !> (compiler flags, linker libraries, etc.) required to correctly enable a package !> to use a core library. -!> +!> !> !>### Available core libraries !> From 2a4dba56c5c0727ea81cceecb7ba22a46fd87007 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 18 Jun 2023 22:12:47 +0200 Subject: [PATCH 616/799] make all homebrew variables `1` instead of `"ON"` --- .github/workflows/meta.yml | 8 ++++---- src/fpm_meta.f90 | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 6370d6dc11..8bf64314d6 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -14,11 +14,11 @@ on: env: CI: "ON" # We can detect this in the build system and other vendors implement it - HOMEBREW_NO_ANALYTICS: "ON" # Make Homebrew installation a little quicker + HOMEBREW_NO_ANALYTICS: 1 # Make Homebrew installation a little quicker HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" - HOMEBREW_NO_GITHUB_API: "ON" - HOMEBREW_NO_INSTALL_CLEANUP: "ON" + HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 + HOMEBREW_NO_GITHUB_API: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 jobs: diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 432d17495f..92b755dd6a 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -5,7 +5,7 @@ !> to use a core library. !> !> -!>### Available core libraries +!>### Available core libraries !> !> - OpenMP !> - MPI From be67bc619f7e206f83e926ce96509fd5789b2fb5 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Sun, 18 Jun 2023 22:18:11 +0200 Subject: [PATCH 617/799] do not check installed dependents --- .github/workflows/meta.yml | 1 + src/fpm_meta.f90 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index 8bf64314d6..86b5215c53 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -19,6 +19,7 @@ env: HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 HOMEBREW_NO_GITHUB_API: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 jobs: diff --git a/src/fpm_meta.f90 b/src/fpm_meta.f90 index 92b755dd6a..f86e3a6b27 100644 --- a/src/fpm_meta.f90 +++ b/src/fpm_meta.f90 @@ -3,7 +3,7 @@ !> This is a wrapper data type that encapsulate all pre-processing information !> (compiler flags, linker libraries, etc.) required to correctly enable a package !> to use a core library. -!> +!> !> !>### Available core libraries !> From bf88610a82d0ec44e6ee89c7c7f7b53b0e3e48db Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Mon, 19 Jun 2023 08:26:17 +0200 Subject: [PATCH 618/799] fpm_filesystem.F90: fix broken resolve conflicts --- src/fpm_filesystem.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fpm_filesystem.F90 b/src/fpm_filesystem.F90 index 9d2d8f896e..81c5628e40 100644 --- a/src/fpm_filesystem.F90 +++ b/src/fpm_filesystem.F90 @@ -14,8 +14,7 @@ module fpm_filesystem public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, list_files, get_local_prefix, & mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, fileopen, fileclose, & filewrite, warnwrite, parent_dir, is_hidden_file, read_lines, read_lines_expanded, which, run, & - LINE_BUFFER_LEN, os_delete_dir, is_absolute_path, get_home, execute_and_read_output, & - get_dos_path + os_delete_dir, is_absolute_path, get_home, execute_and_read_output, get_dos_path #ifndef FPM_BOOTSTRAP interface From 16aca47389449a1b5510e341eea8af48c47359bc Mon Sep 17 00:00:00 2001 From: "John S. Urban" Date: Wed, 21 Jun 2023 06:23:37 -0400 Subject: [PATCH 619/799] update jonquil version --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 90b1712f66..1da1a00dcf 100644 --- a/fpm.toml +++ b/fpm.toml @@ -17,7 +17,7 @@ M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" fortran-regex.git = "https://github.com/perazz/fortran-regex" fortran-regex.tag = "1.1.2" jonquil.git = "https://github.com/toml-f/jonquil" -jonquil.rev = "4c27c8c1e411fa8790dffcf8c3fa7a27b6322273" +jonquil.rev = "4fbd4cf34d577c0fd25e32667ee9e41bf231ece8" [[test]] name = "cli-test" From f87fac1d688a411936cf5cbc0eb0c4845056470b Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 22 Jun 2023 00:02:18 +0700 Subject: [PATCH 620/799] Clean up clean command --- src/fpm.f90 | 35 +++++++++++++++++++---------------- src/fpm_command_line.f90 | 16 +++++----------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 9e82b91d97..50d39a8842 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -21,10 +21,12 @@ module fpm use fpm_manifest, only : get_package_data, package_config_t use fpm_meta, only : resolve_metapackages use fpm_error, only : error_t, fatal_error, fpm_stop -use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, & - & stdout=>output_unit, & - & stderr=>error_unit +use, intrinsic :: iso_fortran_env, only : stdin => input_unit, & + & stdout => output_unit, & + & stderr => error_unit use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer +use fpm_environment, only: os_is_unix + implicit none private public :: cmd_build, cmd_run, cmd_clean @@ -676,27 +678,28 @@ subroutine delete_skip(is_unix) end do end subroutine delete_skip +!> Delete the build directory including or excluding dependencies. subroutine cmd_clean(settings) - !> fpm clean called + !> Settings for the clean command. class(fpm_clean_settings), intent(in) :: settings - ! character(len=:), allocatable :: dir - ! type(string_t), allocatable :: files(:) - character(len=1) :: response + + character :: user_response + if (is_dir('build')) then - ! remove the entire build directory + ! Remove the entire build directory if (settings%clean_call) then - call os_delete_dir(settings%is_unix, 'build') - return + call os_delete_dir(os_is_unix(), 'build'); return end if - ! remove the build directory but skip dependencies + + ! Remove the build directory but skip dependencies if (settings%clean_skip) then - call delete_skip(settings%is_unix) - return + call delete_skip(os_is_unix()); return end if - ! prompt to remove the build directory but skip dependencies + + ! Prompt to remove the build directory but skip dependencies write(stdout, '(A)', advance='no') "Delete build, excluding dependencies (y/n)? " - read(stdin, '(A1)') response - if (lower(response) == 'y') call delete_skip(settings%is_unix) + read(stdin, '(A1)') user_response + if (lower(user_response) == 'y') call delete_skip(os_is_unix()) else write (stdout, '(A)') "fpm: No build directory found." end if diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index f1ced79308..306b79a535 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -23,7 +23,7 @@ !> ``fpm-help`` and ``fpm --list`` help pages below to make sure the help output !> is complete and consistent as well. module fpm_command_line -use fpm_environment, only : get_os_type, get_env, os_is_unix, & +use fpm_environment, only : get_os_type, get_env, & OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified @@ -112,10 +112,8 @@ module fpm_command_line end type type, extends(fpm_cmd_settings) :: fpm_clean_settings - logical :: is_unix - character(len=:), allocatable :: calling_dir ! directory clean called from - logical :: clean_skip=.false. - logical :: clean_call=.false. + logical :: clean_skip = .false. + logical :: clean_call = .false. end type type, extends(fpm_build_settings) :: fpm_publish_settings @@ -217,7 +215,6 @@ subroutine get_command_line_settings(cmd_settings) character(len=4096) :: cmdarg integer :: i integer :: os - logical :: is_unix type(fpm_install_settings), allocatable :: install_settings type(version_t) :: version character(len=:), allocatable :: common_args, compiler_args, run_args, working_dir, & @@ -243,7 +240,6 @@ subroutine get_command_line_settings(cmd_settings) case (OS_UNKNOWN); os_type = "OS Type: Unknown" case default ; os_type = "OS Type: UNKNOWN" end select - is_unix = os_is_unix(os) ! Get current release version version = fpm_version() @@ -588,7 +584,7 @@ subroutine get_command_line_settings(cmd_settings) & build_tests=.true., & & name=names, & & runner=val_runner, & - & verbose=lget('verbose') ) + & verbose=lget('verbose')) case('update') call set_args(common_args // ' --fetch-only F --clean F', & @@ -613,10 +609,8 @@ subroutine get_command_line_settings(cmd_settings) allocate(fpm_clean_settings :: cmd_settings) call get_current_directory(working_dir, error) cmd_settings=fpm_clean_settings( & - & is_unix=is_unix, & - & calling_dir=working_dir, & & clean_skip=lget('skip'), & - clean_call=lget('all')) + & clean_call=lget('all')) case('publish') call set_args(common_args // compiler_args //'& From e64753cd89fc527f6927cbb0d796db25e1b8c40c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 22 Jun 2023 13:54:02 +0700 Subject: [PATCH 621/799] Throw error when both --skip and --all were specified, rename clean_call to clean_all, clean up code --- src/fpm.f90 | 2 +- src/fpm_command_line.f90 | 23 +++++++++++++++++------ test/cli_test/cli_test.f90 | 3 +-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 50d39a8842..67a71f5e3a 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -687,7 +687,7 @@ subroutine cmd_clean(settings) if (is_dir('build')) then ! Remove the entire build directory - if (settings%clean_call) then + if (settings%clean_all) then call os_delete_dir(os_is_unix(), 'build'); return end if diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 306b79a535..427b7b36b2 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -113,7 +113,7 @@ module fpm_command_line type, extends(fpm_cmd_settings) :: fpm_clean_settings logical :: clean_skip = .false. - logical :: clean_call = .false. + logical :: clean_all = .false. end type type, extends(fpm_build_settings) :: fpm_publish_settings @@ -606,11 +606,22 @@ subroutine get_command_line_settings(cmd_settings) & ' --skip' // & & ' --all', & help_clean, version_text) - allocate(fpm_clean_settings :: cmd_settings) - call get_current_directory(working_dir, error) - cmd_settings=fpm_clean_settings( & - & clean_skip=lget('skip'), & - & clean_call=lget('all')) + + block + logical :: skip, clean_all + + skip = lget('skip') + clean_all = lget('all') + + if (all([skip, clean_all])) then + call fpm_stop(6, 'Do not specify both --skip and --all options on the clean subcommand.') + end if + + allocate(fpm_clean_settings :: cmd_settings) + cmd_settings=fpm_clean_settings( & + & clean_skip=skip, & + & clean_all=clean_all) + end block case('publish') call set_args(common_args // compiler_args //'& diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index f5336b62ca..2ba146729a 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -264,7 +264,7 @@ subroutine parse() if (allocated(settings%args)) act_args=settings%args type is (fpm_clean_settings) act_c_s=settings%clean_skip - act_c_a=settings%clean_call + act_c_a=settings%clean_all type is (fpm_install_settings) type is (fpm_publish_settings) act_show_v=settings%show_package_version @@ -275,7 +275,6 @@ subroutine parse() open(file='_test_cli',newunit=lun,delim='quote') write(lun,nml=act_cli,delim='quote') -!!write(*,nml=act_cli) close(unit=lun) end subroutine parse From ae27097707d1e7f9b856e76f93689d0175c2cc71 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 22 Jun 2023 14:47:05 +0700 Subject: [PATCH 622/799] Add option for clearing the registry cache --- src/fpm.f90 | 15 ++++++++++++--- src/fpm_command_line.f90 | 7 +++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 67a71f5e3a..c6deada198 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -26,6 +26,7 @@ module fpm & stderr => error_unit use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer use fpm_environment, only: os_is_unix +use fpm_settings, only: fpm_global_settings, get_global_settings implicit none private @@ -684,15 +685,23 @@ subroutine cmd_clean(settings) class(fpm_clean_settings), intent(in) :: settings character :: user_response + type(fpm_global_settings) :: global_settings + type(error_t), allocatable :: error + + ! Clear registry cache + if (settings%registry_cache) then + call get_global_settings(global_settings, error) + if (allocated(error)) return + + call os_delete_dir(os_is_unix(), global_settings%registry_settings%cache_path) + end if if (is_dir('build')) then ! Remove the entire build directory if (settings%clean_all) then call os_delete_dir(os_is_unix(), 'build'); return - end if - ! Remove the build directory but skip dependencies - if (settings%clean_skip) then + else if (settings%clean_skip) then call delete_skip(os_is_unix()); return end if diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 427b7b36b2..5efede34f6 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -114,6 +114,7 @@ module fpm_command_line type, extends(fpm_cmd_settings) :: fpm_clean_settings logical :: clean_skip = .false. logical :: clean_all = .false. + logical :: registry_cache = .false. end type type, extends(fpm_build_settings) :: fpm_publish_settings @@ -603,6 +604,7 @@ subroutine get_command_line_settings(cmd_settings) case('clean') call set_args(common_args // & + & ' --registry-cache' // & & ' --skip' // & & ' --all', & help_clean, version_text) @@ -618,8 +620,9 @@ subroutine get_command_line_settings(cmd_settings) end if allocate(fpm_clean_settings :: cmd_settings) - cmd_settings=fpm_clean_settings( & - & clean_skip=skip, & + cmd_settings = fpm_clean_settings( & + & registry_cache=lget('registry-cache'), & + & clean_skip=skip, & & clean_all=clean_all) end block From cc2a5684c51b90cffa2787380f692c8f956a308a Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 22 Jun 2023 14:55:27 +0700 Subject: [PATCH 623/799] Add test --- test/cli_test/cli_test.f90 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/cli_test/cli_test.f90 b/test/cli_test/cli_test.f90 index 2ba146729a..975b80812b 100644 --- a/test/cli_test/cli_test.f90 +++ b/test/cli_test/cli_test.f90 @@ -29,6 +29,7 @@ program main logical :: w_t,act_w_t ; namelist/act_cli/act_w_t logical :: c_s,act_c_s ; namelist/act_cli/act_c_s logical :: c_a,act_c_a ; namelist/act_cli/act_c_a +logical :: reg_c,act_reg_c ; namelist/act_cli/act_reg_c logical :: show_v,act_show_v ; namelist/act_cli/act_show_v logical :: show_u_d,act_show_u_d; namelist/act_cli/act_show_u_d logical :: dry_run,act_dry_run ; namelist/act_cli/act_dry_run @@ -36,7 +37,7 @@ program main character(len=:), allocatable :: profile,act_profile ; namelist/act_cli/act_profile character(len=:), allocatable :: args,act_args ; namelist/act_cli/act_args -namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,name,profile,args,show_v,show_u_d,dry_run,token +namelist/expected/cmd,cstat,estat,w_e,w_t,c_s,c_a,reg_c,name,profile,args,show_v,show_u_d,dry_run,token integer :: lun logical,allocatable :: tally(:) logical,allocatable :: subtally(:) @@ -75,6 +76,7 @@ program main 'CMD="clean", NAME=, ARGS="",', & 'CMD="clean --skip", C_S=T, NAME=, ARGS="",', & 'CMD="clean --all", C_A=T, NAME=, ARGS="",', & +'CMD="clean --registry-cache", REG_C=T, NAME=, ARGS="",', & 'CMD="publish --token abc --show-package-version", SHOW_V=T, NAME=, token="abc",ARGS="",', & 'CMD="publish --token abc --show-upload-data", SHOW_U_D=T, NAME=, token="abc",ARGS="",', & 'CMD="publish --token abc --dry-run", DRY_RUN=T, NAME=, token="abc",ARGS="",', & @@ -111,6 +113,7 @@ program main w_t=.false. ! --test c_s=.false. ! --skip c_a=.false. ! --all + reg_c=.false. ! --registry-cache show_v=.false. ! --show-package-version show_u_d=.false. ! --show-upload-data dry_run=.false. ! --dry-run @@ -134,6 +137,7 @@ program main act_w_t=.false. act_c_s=.false. act_c_a=.false. + act_reg_c=.false. act_show_v=.false. act_show_u_d=.false. act_dry_run=.false. @@ -148,6 +152,9 @@ program main subtally=[logical ::] call test_test('NAME',all(act_name==name)) call test_test('PROFILE',act_profile==profile) + call test_test('SKIP',act_c_s.eqv.c_s) + call test_test('ALL',act_c_a.eqv.c_a) + call test_test('REGISTRY-CACHE',act_reg_c.eqv.reg_c) call test_test('WITH_EXPECTED',act_w_e.eqv.w_e) call test_test('WITH_TESTED',act_w_t.eqv.w_t) call test_test('WITH_TEST',act_w_t.eqv.w_t) @@ -241,6 +248,7 @@ subroutine parse() act_w_t=.false. act_c_s=.false. act_c_a=.false. +act_reg_c=.false. act_show_v=.false. act_show_u_d=.false. act_dry_run=.false. @@ -265,6 +273,7 @@ subroutine parse() type is (fpm_clean_settings) act_c_s=settings%clean_skip act_c_a=settings%clean_all + act_reg_c=settings%registry_cache type is (fpm_install_settings) type is (fpm_publish_settings) act_show_v=settings%show_package_version From 56da54f8d0155d107c3f86498814985036b9b45e Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 22 Jun 2023 15:28:42 +0700 Subject: [PATCH 624/799] Add to fpm clean --- src/fpm.f90 | 3 ++- src/fpm_command_line.f90 | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index c6deada198..9aff72443b 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -679,7 +679,8 @@ subroutine delete_skip(is_unix) end do end subroutine delete_skip -!> Delete the build directory including or excluding dependencies. +!> Delete the build directory including or excluding dependencies. Can be used +!> to clear the registry cache. subroutine cmd_clean(settings) !> Settings for the clean command. class(fpm_clean_settings), intent(in) :: settings diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90 index 5efede34f6..cf98d3f624 100644 --- a/src/fpm_command_line.f90 +++ b/src/fpm_command_line.f90 @@ -764,7 +764,7 @@ subroutine set_help() ' [--list] [--compiler COMPILER_NAME] [-- ARGS] ', & ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & - ' clean [--skip] [--all] ', & + ' clean [--skip] [--all] [--registry-cache] ', & ' publish [--token TOKEN] [--show-package-version] [--show-upload-data] ', & ' [--dry-run] [--verbose] ', & ' '] @@ -889,7 +889,7 @@ subroutine set_help() ' list [--list] ', & ' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] ', & ' [options] ', & - ' clean [--skip] [--all] ', & + ' clean [--skip] [--all] [--registry-cache] ', & ' publish [--token TOKEN] [--show-package-version] [--show-upload-data] ', & ' [--dry-run] [--verbose] ', & ' ', & @@ -901,12 +901,15 @@ subroutine set_help() help_text_flag, & ' --list List candidates instead of building or running them. On ', & ' the fpm(1) command this shows a brief list of subcommands.', & - ' --runner CMD Provides a command to prefix program execution paths. ', & + ' --runner CMD Provides a command to prefix program execution paths. ', & ' -- ARGS Arguments to pass to executables. ', & ' --skip Delete directories in the build/ directory without ', & - ' prompting, but skip dependencies. ', & + ' prompting, but skip dependencies. Cannot be used together ', & + ' with --all. ', & ' --all Delete directories in the build/ directory without ', & - ' prompting, including dependencies. ', & + ' prompting, including dependencies. Cannot be used together', & + ' with --skip. ', & + ' --registry-cache Delete registry cache. ', & ' ', & 'VALID FOR ALL SUBCOMMANDS ', & ' --help Show help text and exit ', & @@ -1364,10 +1367,12 @@ subroutine set_help() 'DESCRIPTION', & ' Prompts the user to confirm deletion of the build. If affirmative,', & ' directories in the build/ directory are deleted, except dependencies.', & + ' Use the --registry-cache option to delete the registry cache.', & '', & 'OPTIONS', & - ' --skip delete the build without prompting but skip dependencies.', & - ' --all delete the build without prompting including dependencies.', & + ' --skip Delete the build without prompting but skip dependencies.', & + ' --all Delete the build without prompting including dependencies.', & + ' --registry-cache Delete registry cache.', & '' ] help_publish=[character(len=80) :: & 'NAME', & From f83762ad842659cf923488819c003bf736422796 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 22 Jun 2023 15:55:15 +0700 Subject: [PATCH 625/799] Check allocation status before allocating --- src/fpm_settings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fpm_settings.f90 b/src/fpm_settings.f90 index fe4b0748fa..a11abb8565 100644 --- a/src/fpm_settings.f90 +++ b/src/fpm_settings.f90 @@ -113,7 +113,7 @@ subroutine get_global_settings(global_settings, error) subroutine use_default_registry_settings(global_settings) type(fpm_global_settings), intent(inout) :: global_settings - allocate (global_settings%registry_settings) + if (.not. allocated(global_settings%registry_settings)) allocate (global_settings%registry_settings) global_settings%registry_settings%url = official_registry_base_url global_settings%registry_settings%cache_path = join_path(global_settings%path_to_config_folder_or_empty(), & & 'dependencies') From e9cddadcdc3a8b433fbe0298a39a6bb92a87ab3c Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:06:31 +0200 Subject: [PATCH 626/799] add `preprocess_config` to the dependency struct --- src/fpm/manifest/dependency.f90 | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 75f5f5d10d..606c758897 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -32,6 +32,8 @@ module fpm_manifest_dependency use fpm_manifest_metapackages, only: metapackage_config_t, is_meta_package, new_meta_config, & metapackage_request_t, new_meta_request use fpm_versioning, only: version_t, new_version + use fpm_strings, only: string_t + use fpm_manifest_preprocess implicit none private @@ -55,6 +57,9 @@ module fpm_manifest_dependency !> The latest version is used if not specified. type(version_t), allocatable :: requested_version + !> Requested macros for the dependency + type(preprocess_config_t), allocatable :: preprocess(:) + !> Git descriptor type(git_target_t), allocatable :: git @@ -87,6 +92,8 @@ subroutine new_dependency(self, table, root, error) character(len=:), allocatable :: uri, value, requested_version + type(toml_table), pointer :: child + call check(table, error) if (allocated(error)) return @@ -136,6 +143,13 @@ subroutine new_dependency(self, table, root, error) if (allocated(error)) return end if + !> Get optional preprocessor directives + call get_value(table, "preprocess", child, requested=.false.) + if (associated(child)) then + call new_preprocessors(self%preprocess, child, error) + if (allocated(error)) return + end if + end subroutine new_dependency !> Check local schema for allowed entries @@ -158,7 +172,8 @@ subroutine check(table, error) "git", & "tag", & "branch", & - "rev" & + "rev", & + "preprocess" & & ] call table%get_key(name) @@ -170,6 +185,7 @@ subroutine check(table, error) end if call check_keys(table, valid_keys, error) + print *, 'check keys ',allocated(error) if (allocated(error)) return if (table%has_key("path") .and. table%has_key("git")) then From c480ca11af818ad33c656dd876905c87ef07ad0e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:06:45 +0200 Subject: [PATCH 627/799] enable check for `child` node in toml, not only string values --- src/fpm/toml.f90 | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/fpm/toml.f90 b/src/fpm/toml.f90 index f8d8ea2420..71cb148330 100644 --- a/src/fpm/toml.f90 +++ b/src/fpm/toml.f90 @@ -123,6 +123,7 @@ subroutine check_keys(table, valid_keys, error) type(error_t), allocatable, intent(out) :: error type(toml_key), allocatable :: keys(:) + type(toml_table), pointer :: child character(:), allocatable :: name, value, valid_keys_string integer :: ikey, ivalid @@ -143,12 +144,18 @@ subroutine check_keys(table, valid_keys, error) end if ! Check if value can be mapped or else (wrong type) show error message with the error location. - ! Right now, it can only be mapped to a string, but this can be extended in the future. + ! Right now, it can only be mapped to a string or to a child node, but this can be extended in the future. call get_value(table, keys(ikey)%key, value) if (.not. allocated(value)) then - allocate (error) - error%message = "'"//name//"' has an invalid '"//keys(ikey)%key//"' entry." - return + + ! If value is not a string, check if it is a child node + call get_value(table, keys(ikey)%key, child) + + if (.not.associated(child)) then + allocate (error) + error%message = "'"//name//"' has an invalid '"//keys(ikey)%key//"' entry." + return + endif end if end do From 44076f4a0be0ed32abeebd7079544b0c6f5deece Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:07:01 +0200 Subject: [PATCH 628/799] create simple test program --- .../preprocess_per_dependency/app/main.f90 | 8 ++++++++ .../crate/utils/fpm.toml | 5 +++++ .../crate/utils/src/say_hello.f90 | 19 +++++++++++++++++++ .../preprocess_per_dependency/fpm.toml | 4 ++++ 4 files changed, 36 insertions(+) create mode 100644 example_packages/preprocess_per_dependency/app/main.f90 create mode 100644 example_packages/preprocess_per_dependency/crate/utils/fpm.toml create mode 100644 example_packages/preprocess_per_dependency/crate/utils/src/say_hello.f90 create mode 100644 example_packages/preprocess_per_dependency/fpm.toml diff --git a/example_packages/preprocess_per_dependency/app/main.f90 b/example_packages/preprocess_per_dependency/app/main.f90 new file mode 100644 index 0000000000..aed30cd33d --- /dev/null +++ b/example_packages/preprocess_per_dependency/app/main.f90 @@ -0,0 +1,8 @@ +program hello_fpm + use utils, only: say_hello + integer :: ierr + + call say_hello(ierr) + stop ierr ! ierr==0 if DEPENDENCY_MACRO is defined + +end program hello_fpm diff --git a/example_packages/preprocess_per_dependency/crate/utils/fpm.toml b/example_packages/preprocess_per_dependency/crate/utils/fpm.toml new file mode 100644 index 0000000000..f3c03f9934 --- /dev/null +++ b/example_packages/preprocess_per_dependency/crate/utils/fpm.toml @@ -0,0 +1,5 @@ +name = "utils" + +[preprocess] +[preprocess.cpp] +macros = ["X=1"] diff --git a/example_packages/preprocess_per_dependency/crate/utils/src/say_hello.f90 b/example_packages/preprocess_per_dependency/crate/utils/src/say_hello.f90 new file mode 100644 index 0000000000..5f333bab7e --- /dev/null +++ b/example_packages/preprocess_per_dependency/crate/utils/src/say_hello.f90 @@ -0,0 +1,19 @@ +module utils + + implicit none + +contains + + subroutine say_hello(ierr) + integer, intent(out) :: ierr + + ierr = -1 +#ifdef DEPENDENCY_MACRO + ierr = 0 +#endif + + print *, "Dependency macro ", merge(" IS","NOT",ierr==0)," defined" + + end subroutine say_hello + +end module utils diff --git a/example_packages/preprocess_per_dependency/fpm.toml b/example_packages/preprocess_per_dependency/fpm.toml new file mode 100644 index 0000000000..f9f2396e78 --- /dev/null +++ b/example_packages/preprocess_per_dependency/fpm.toml @@ -0,0 +1,4 @@ +name = "preprocess_cpp_deps" + +[dependencies] +utils = { path = "crate/utils" , preprocess.cpp="DEPENDENCY_MACRO" } From 5550f5ed54f13182c3f0b06a8f93aaed7cfd40a9 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:34:44 +0200 Subject: [PATCH 629/799] verify cached preprocessors --- src/fpm.f90 | 21 ++++++++++++++- src/fpm/dependency.f90 | 16 ++++++++++++ src/fpm/manifest/dependency.f90 | 46 +++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 129c4c95dc..8343ff615c 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -109,12 +109,31 @@ subroutine build_model(model, settings, package, error) end associate model%packages(i)%version = package%version%s() + !> Add this dependency's manifest macros + allocate(model%packages(i)%macros(0)) + if (allocated(dependency%preprocess)) then do j = 1, size(dependency%preprocess) if (dependency%preprocess(j)%name == "cpp") then if (.not. has_cpp) has_cpp = .true. if (allocated(dependency%preprocess(j)%macros)) then - model%packages(i)%macros = dependency%preprocess(j)%macros + model%packages(i)%macros = [model%packages(i)%macros, dependency%preprocess(j)%macros] + end if + else + write(stderr, '(a)') 'Warning: Preprocessor ' // package%preprocess(i)%name // & + ' is not supported; will ignore it' + end if + end do + end if + + !> Add this dependency's package-level macros + print *, 'dep preprocess? ',allocated(dep%preprocess),' nam,e=',dep%name + if (allocated(dep%preprocess)) then + do j = 1, size(dep%preprocess) + if (dep%preprocess(j)%name == "cpp") then + if (.not. has_cpp) has_cpp = .true. + if (allocated(dep%preprocess(j)%macros)) then + model%packages(i)%macros = [model%packages(i)%macros, dep%preprocess(j)%macros] end if else write(stderr, '(a)') 'Warning: Preprocessor ' // package%preprocess(i)%name // & diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index af6860a0ac..0b24adcecc 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1187,6 +1187,8 @@ logical function dependency_has_changed(cached, manifest, verbosity, iunit) resu !> Log verbosity integer, intent(in) :: verbosity, iunit + integer :: ip + has_changed = .true. !> All the following entities must be equal for the dependency to not have changed @@ -1219,6 +1221,20 @@ logical function dependency_has_changed(cached, manifest, verbosity, iunit) resu else if (verbosity > 1) write (iunit, out_fmt) "PROJECT DIR has changed presence " end if + if (allocated(cached%preprocess) .eqv. allocated(manifest%preprocess)) then + if (size(cached%preprocess) /= size(manifest%preprocess)) then + if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS has changed size" + return + end if + do ip=1,size(cached%preprocess) + if (cached%preprocess(ip) /= manifest%preprocess(ip)) then + if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS config has changed" + return + end if + end do + else + if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS has changed presence " + end if !> All checks passed: the two dependencies have no differences has_changed = .false. diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 606c758897..2f61ed1336 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -100,6 +100,22 @@ subroutine new_dependency(self, table, root, error) call table%get_key(self%name) call get_value(table, "namespace", self%namespace) + call get_value(table, "v", requested_version) + if (allocated(requested_version)) then + if (.not. allocated(self%requested_version)) allocate (self%requested_version) + call new_version(self%requested_version, requested_version, error) + if (allocated(error)) return + end if + + !> Get optional preprocessor directives + call get_value(table, "preprocess", child, requested=.false.) + print *, 'has preprocess? ',associated(child) + if (associated(child)) then + call new_preprocessors(self%preprocess, child, error) + print *, 'size preprocess ',size(self%preprocess),' error? =',allocated(error) + if (allocated(error)) return + endif + call get_value(table, "path", uri) if (allocated(uri)) then if (get_os_type() == OS_WINDOWS) uri = windows_path(uri) @@ -135,21 +151,6 @@ subroutine new_dependency(self, table, root, error) return end if - call get_value(table, "v", requested_version) - - if (allocated(requested_version)) then - if (.not. allocated(self%requested_version)) allocate (self%requested_version) - call new_version(self%requested_version, requested_version, error) - if (allocated(error)) return - end if - - !> Get optional preprocessor directives - call get_value(table, "preprocess", child, requested=.false.) - if (associated(child)) then - call new_preprocessors(self%preprocess, child, error) - if (allocated(error)) return - end if - end subroutine new_dependency !> Check local schema for allowed entries @@ -163,6 +164,7 @@ subroutine check(table, error) character(len=:), allocatable :: name type(toml_key), allocatable :: list(:) + type(toml_table), pointer :: child !> List of valid keys for the dependency table. character(*), dimension(*), parameter :: valid_keys = [character(24) :: & @@ -185,7 +187,6 @@ subroutine check(table, error) end if call check_keys(table, valid_keys, error) - print *, 'check keys ',allocated(error) if (allocated(error)) return if (table%has_key("path") .and. table%has_key("git")) then @@ -218,6 +219,18 @@ subroutine check(table, error) return end if + ! Check preprocess key + if (table%has_key('preprocess')) then + + call get_value(table, 'preprocess', child) + + if (.not.associated(child)) then + call syntax_error(error, "Dependency '"//name//"' has invalid 'preprocess' entry") + return + end if + + end if + end subroutine check !> Construct new dependency array from a TOML data structure @@ -279,6 +292,7 @@ subroutine new_dependencies(deps, table, root, meta, error) ! Parse as a standard dependency is_meta(idep) = .false. + print *, 'new dependency ',all_deps(idep)%name call new_dependency(all_deps(idep), node, root, error) if (allocated(error)) return From 28c3fd9eba7bc4fc4edbdf8831c29f75430fdef2 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:51:18 +0200 Subject: [PATCH 630/799] compare preprocessing configs in the cached manifest --- src/fpm/dependency.f90 | 4 ++- src/fpm/manifest/preprocess.f90 | 51 +++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 0b24adcecc..86a90c5a98 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -63,6 +63,7 @@ module fpm_dependency use fpm_git, only: git_target_revision, git_target_default, git_revision, operator(==) use fpm_manifest, only: package_config_t, dependency_config_t, get_package_data use fpm_manifest_dependency, only: manifest_has_changed + use fpm_manifest_preprocess, only: operator(==) use fpm_strings, only: string_t, operator(.in.) use fpm_toml, only: toml_table, toml_key, toml_error, toml_serialize, & get_value, set_value, add_table, toml_load, toml_stat @@ -1227,13 +1228,14 @@ logical function dependency_has_changed(cached, manifest, verbosity, iunit) resu return end if do ip=1,size(cached%preprocess) - if (cached%preprocess(ip) /= manifest%preprocess(ip)) then + if (.not.(cached%preprocess(ip) == manifest%preprocess(ip))) then if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS config has changed" return end if end do else if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS has changed presence " + return end if !> All checks passed: the two dependencies have no differences diff --git a/src/fpm/manifest/preprocess.f90 b/src/fpm/manifest/preprocess.f90 index 538652c29a..3f9754725a 100644 --- a/src/fpm/manifest/preprocess.f90 +++ b/src/fpm/manifest/preprocess.f90 @@ -17,7 +17,7 @@ module fpm_manifest_preprocess implicit none private - public :: preprocess_config_t, new_preprocess_config, new_preprocessors + public :: preprocess_config_t, new_preprocess_config, new_preprocessors, operator(==) !> Configuration meta data for a preprocessor type :: preprocess_config_t @@ -41,6 +41,10 @@ module fpm_manifest_preprocess end type preprocess_config_t + interface operator(==) + module procedure preprocess_is_same + end interface + contains !> Construct a new preprocess configuration from TOML data structure @@ -154,7 +158,7 @@ subroutine info(self, unit, verbosity) pr = 1 end if - if (pr < 1) return + if (pr < 1) return write(unit, fmt) "Preprocessor" if (allocated(self%name)) then @@ -181,4 +185,47 @@ subroutine info(self, unit, verbosity) end subroutine info + logical function preprocess_is_same(this,that) + class(preprocess_config_t), intent(in) :: this + class(preprocess_config_t), intent(in) :: that + + integer :: istr + + preprocess_is_same = .false. + + select type (other=>that) + type is (preprocess_config_t) + if (allocated(this%name).neqv.allocated(other%name)) return + if (allocated(this%name)) then + if (.not.(this%name==other%name)) return + endif + if (.not.(allocated(this%suffixes).eqv.allocated(other%suffixes))) return + if (allocated(this%suffixes)) then + do istr=1,size(this%suffixes) + if (.not.(this%suffixes(istr)%s==other%suffixes(istr)%s)) return + end do + end if + if (.not.(allocated(this%directories).eqv.allocated(other%directories))) return + if (allocated(this%directories)) then + do istr=1,size(this%directories) + if (.not.(this%directories(istr)%s==other%directories(istr)%s)) return + end do + end if + if (.not.(allocated(this%macros).eqv.allocated(other%macros))) return + if (allocated(this%macros)) then + do istr=1,size(this%macros) + if (.not.(this%macros(istr)%s==other%macros(istr)%s)) return + end do + end if + + class default + ! Not the same type + return + end select + + !> All checks passed! + preprocess_is_same = .true. + + end function preprocess_is_same + end module fpm_manifest_preprocess From 011877ff4e52990b5c3823d2651eacb794066587 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:51:33 +0200 Subject: [PATCH 631/799] fix example macro input --- example_packages/preprocess_per_dependency/fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_packages/preprocess_per_dependency/fpm.toml b/example_packages/preprocess_per_dependency/fpm.toml index f9f2396e78..4730973ab1 100644 --- a/example_packages/preprocess_per_dependency/fpm.toml +++ b/example_packages/preprocess_per_dependency/fpm.toml @@ -1,4 +1,4 @@ name = "preprocess_cpp_deps" [dependencies] -utils = { path = "crate/utils" , preprocess.cpp="DEPENDENCY_MACRO" } +utils = { path = "crate/utils" , preprocess.cpp.macros=["DEPENDENCY_MACRO"] } From e29f3943c243b4e13316b32199d48bf773faee06 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 16:53:28 +0200 Subject: [PATCH 632/799] add package test to CI --- ci/run_tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 987b282449..d84a00f1c5 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -150,6 +150,10 @@ pushd preprocess_cpp_deps "$fpm" build popd +pushd preprocess_per_dependency +"$fpm" run +popd + pushd preprocess_hello "$fpm" build popd From 826b1825be45d348d67fcea7f395b7d527b9ec76 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 17:15:14 +0200 Subject: [PATCH 633/799] fix unallocated preprocess in test --- src/fpm/dependency.f90 | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/fpm/dependency.f90 b/src/fpm/dependency.f90 index 86a90c5a98..52e5c6ec12 100644 --- a/src/fpm/dependency.f90 +++ b/src/fpm/dependency.f90 @@ -1223,16 +1223,18 @@ logical function dependency_has_changed(cached, manifest, verbosity, iunit) resu if (verbosity > 1) write (iunit, out_fmt) "PROJECT DIR has changed presence " end if if (allocated(cached%preprocess) .eqv. allocated(manifest%preprocess)) then - if (size(cached%preprocess) /= size(manifest%preprocess)) then - if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS has changed size" - return - end if - do ip=1,size(cached%preprocess) - if (.not.(cached%preprocess(ip) == manifest%preprocess(ip))) then - if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS config has changed" + if (allocated(cached%preprocess)) then + if (size(cached%preprocess) /= size(manifest%preprocess)) then + if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS has changed size" return - end if - end do + end if + do ip=1,size(cached%preprocess) + if (.not.(cached%preprocess(ip) == manifest%preprocess(ip))) then + if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS config has changed" + return + end if + end do + endif else if (verbosity > 1) write (iunit, out_fmt) "PREPROCESS has changed presence " return From 3df03c22535aa0270272f4409c223cc1c8a916b7 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Tue, 4 Jul 2023 17:19:21 +0200 Subject: [PATCH 634/799] cleanup cleanup --- src/fpm.f90 | 1 - src/fpm/manifest/dependency.f90 | 3 --- test/fpm_test/test_package_dependencies.f90 | 1 - 3 files changed, 5 deletions(-) diff --git a/src/fpm.f90 b/src/fpm.f90 index 8343ff615c..0a2712e612 100644 --- a/src/fpm.f90 +++ b/src/fpm.f90 @@ -127,7 +127,6 @@ subroutine build_model(model, settings, package, error) end if !> Add this dependency's package-level macros - print *, 'dep preprocess? ',allocated(dep%preprocess),' nam,e=',dep%name if (allocated(dep%preprocess)) then do j = 1, size(dep%preprocess) if (dep%preprocess(j)%name == "cpp") then diff --git a/src/fpm/manifest/dependency.f90 b/src/fpm/manifest/dependency.f90 index 2f61ed1336..de4f104db9 100644 --- a/src/fpm/manifest/dependency.f90 +++ b/src/fpm/manifest/dependency.f90 @@ -109,10 +109,8 @@ subroutine new_dependency(self, table, root, error) !> Get optional preprocessor directives call get_value(table, "preprocess", child, requested=.false.) - print *, 'has preprocess? ',associated(child) if (associated(child)) then call new_preprocessors(self%preprocess, child, error) - print *, 'size preprocess ',size(self%preprocess),' error? =',allocated(error) if (allocated(error)) return endif @@ -292,7 +290,6 @@ subroutine new_dependencies(deps, table, root, meta, error) ! Parse as a standard dependency is_meta(idep) = .false. - print *, 'new dependency ',all_deps(idep)%name call new_dependency(all_deps(idep), node, root, error) if (allocated(error)) return diff --git a/test/fpm_test/test_package_dependencies.f90 b/test/fpm_test/test_package_dependencies.f90 index 4f645750b5..0a5877a172 100644 --- a/test/fpm_test/test_package_dependencies.f90 +++ b/test/fpm_test/test_package_dependencies.f90 @@ -334,7 +334,6 @@ subroutine test_non_updated_dependencies(error) return end if - ! Test that dependency 3 is flagged as "not update" if (manifest_deps%dep(3)%update) then call test_failed(error, "Updated dependency (git rev) detected, should not be") From 69d3ea74a56e17df67c1d80ee2cbe84ceb89e7a6 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Fri, 7 Jul 2023 15:03:17 +0200 Subject: [PATCH 635/799] fix unallocated targets array --- src/fpm_targets.f90 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index bc31f1594b..df0d58810f 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -198,15 +198,14 @@ subroutine build_target_list(targets,model) character(:), allocatable :: exe_dir, compile_flags logical :: with_lib + ! Initialize targets + allocate(targets(0)) + ! Check for empty build (e.g. header-only lib) n_source = sum([(size(model%packages(j)%sources), & j=1,size(model%packages))]) - if (n_source < 1) then - allocate(targets(0)) - return - end if - + if (n_source < 1) return with_lib = any([((model%packages(j)%sources(i)%unit_scope == FPM_SCOPE_LIB, & i=1,size(model%packages(j)%sources)), & @@ -826,7 +825,7 @@ subroutine resolve_target_linking(targets, model) if (.not.allocated(target%compile_flags)) allocate(character(len=0) :: target%compile_flags) target%compile_flags = target%compile_flags//' ' - + select case (target%target_type) case (FPM_TARGET_C_OBJECT) target%compile_flags = target%compile_flags//model%c_compile_flags @@ -835,7 +834,7 @@ subroutine resolve_target_linking(targets, model) case default target%compile_flags = target%compile_flags//model%fortran_compile_flags & & // get_feature_flags(model%compiler, target%features) - end select + end select !> Get macros as flags. target%compile_flags = target%compile_flags // get_macros(model%compiler%id, & From ce9299636fca6fb50850ebf4cb6189ba2f3e3cc3 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Wed, 9 Aug 2023 08:17:49 +0200 Subject: [PATCH 636/799] remove `/en/` locale from paths --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a3e3be3f77..3670cede53 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ non-Fortran related package manager. **Website: ** -## [Download](https://fpm.fortran-lang.org/en/install/index.html) +## [Download](https://fpm.fortran-lang.org/install/index.html) Fpm is available on many platforms and through multiple package managers, see our Documentation -webpage for a list of **[All Supported Installations](https://fpm.fortran-lang.org/en/install/index.html)**. +webpage for a list of **[All Supported Installations](https://fpm.fortran-lang.org/install/index.html)**. The easiest installation routes are shown below. @@ -86,9 +86,9 @@ Binary distributions are available for MacOS 11 (Catalina) and 12 (Big Sur) for Fpm should be available and functional after those steps. For more details checkout the tap [here](https://github.com/fortran-lang/homebrew-fortran). -## [Get started](https://fpm.fortran-lang.org/en/tutorial/index.html) +## [Get started](https://fpm.fortran-lang.org/tutorial/index.html) -**Follow our [Quickstart Tutorial](https://fpm.fortran-lang.org/en/tutorial/hello-fpm.html) to get familiar with fpm**. +**Follow our [Quickstart Tutorial](https://fpm.fortran-lang.org/tutorial/hello-fpm.html) to get familiar with fpm**. ### Start a new project @@ -118,7 +118,7 @@ arguments can also be passed to the executable(s) or test(s) with the option `-- some arguments`. See additional instructions in the [Packaging guide](PACKAGING.md) or -the [manifest reference](https://fpm.fortran-lang.org/en/spec/manifest.html). +the [manifest reference](https://fpm.fortran-lang.org/spec/manifest.html).