From 7d42784963608667ec6f1819b38c0fd7beb00abf Mon Sep 17 00:00:00 2001 From: minhqdao Date: Fri, 23 Dec 2022 14:09:47 +0100 Subject: [PATCH 001/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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/142] 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 1793de2abb4f3bcfbdfa1a304888e3db9568651d Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 22 Feb 2023 02:06:03 +0100 Subject: [PATCH 100/142] 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 101/142] 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 102/142] 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 103/142] 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 104/142] 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 105/142] 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 106/142] 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 7230efa56e89a7139e5ffe23d2da3020c0cdc422 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 1 Mar 2023 15:50:40 +0100 Subject: [PATCH 107/142] 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 2af576c25cede932000611bd26181b973358025c Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 1 Mar 2023 16:12:15 +0100 Subject: [PATCH 108/142] 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 109/142] 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 110/142] 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 111/142] 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 112/142] 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 113/142] 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 114/142] 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 115/142] 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 116/142] 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 117/142] 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 118/142] 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 119/142] 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 120/142] 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 121/142] 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 122/142] 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 123/142] 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 124/142] 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 125/142] 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 e02b917b6f953a98918b7806695fa42f0457e324 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Wed, 8 Mar 2023 16:20:40 +0100 Subject: [PATCH 126/142] 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 127/142] 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 128/142] 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 129/142] 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 130/142] 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 131/142] 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 132/142] 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 133/142] 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 6c065ce0519b5325dee7103c1e567a92c78df1f4 Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 14:30:04 +0700 Subject: [PATCH 134/142] 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 225a4620ced418f21277115066682e301ff450dc Mon Sep 17 00:00:00 2001 From: minhqdao Date: Sun, 2 Apr 2023 18:11:06 +0700 Subject: [PATCH 135/142] 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 136/142] 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 137/142] 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 138/142] 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 139/142] 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 140/142] 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 141/142] 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 ed27f9bf1cb20c8fb12d2570397cf711a162c77f Mon Sep 17 00:00:00 2001 From: minhqdao Date: Thu, 6 Apr 2023 23:15:59 +0700 Subject: [PATCH 142/142] 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