!> Implementation of the meta data for dependencies. !> !> A dependency table can currently have the following fields !> !>```toml !>[dependencies] !>"dep1" = { git = "url" } !>"dep2" = { git = "url", branch = "name" } !>"dep3" = { git = "url", tag = "name" } !>"dep4" = { git = "url", rev = "sha1" } !>"dep0" = { path = "path" } !>``` !> !> To reduce the amount of boilerplate code this module provides two constructors !> for dependency types, one basic for an actual dependency (inline) table !> and another to collect all dependency objects from a dependencies table, !> which is handling the allocation of the objects and is forwarding the !> individual dependency tables to their respective constructors. !> The usual entry point should be the constructor for the super table. !> !> This objects contains a target to retrieve required `fpm` projects to !> build the target declaring the dependency. !> 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, & & git_target_revision, git_target_default, operator(==) use fpm_toml, only : toml_table, toml_key, toml_stat, get_value use fpm_filesystem, only: windows_path use fpm_environment, only: get_os_type, OS_WINDOWS implicit none private public :: dependency_config_t, new_dependency, new_dependencies, manifest_has_changed !> Configuration meta data for a dependency type :: dependency_config_t !> Name of the dependency character(len=:), allocatable :: name !> Local target character(len=:), allocatable :: path !> Git descriptor type(git_target_t), allocatable :: git contains !> Print information on this instance procedure :: info end type dependency_config_t contains !> Construct a new dependency configuration from a TOML data structure subroutine new_dependency(self, table, root, error) !> Instance of the dependency configuration type(dependency_config_t), intent(out) :: self !> Instance of the TOML data structure type(toml_table), intent(inout) :: table !> Root directory of the manifest character(*), intent(in), optional :: root !> Error handling type(error_t), allocatable, intent(out) :: error character(len=:), allocatable :: url, obj call check(table, error) if (allocated(error)) return call table%get_key(self%name) 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, "tag", obj) if (allocated(obj)) then self%git = git_target_tag(url, obj) 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) 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) end if end if if (.not.allocated(self%git)) then self%git = git_target_default(url) end if end if end subroutine new_dependency !> Check local schema for allowed entries subroutine check(table, error) !> Instance of the TOML data structure type(toml_table), intent(inout) :: table !> Error handling type(error_t), allocatable, intent(out) :: error 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. call table%get_key(name) call table%get_keys(list) if (size(list) < 1) then 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) 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 end do if (allocated(error)) return if (.not.url_present) 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") end if end subroutine check !> Construct new dependency array from a TOML data structure subroutine new_dependencies(deps, table, root, error) !> Instance of the dependency configuration type(dependency_config_t), allocatable, intent(out) :: deps(:) !> Instance of the TOML data structure type(toml_table), intent(inout) :: table !> Root directory of the manifest character(*), intent(in), optional :: root !> Error handling type(error_t), allocatable, intent(out) :: error type(toml_table), pointer :: node type(toml_key), allocatable :: list(:) integer :: idep, stat call table%get_keys(list) ! An empty table is okay if (size(list) < 1) return 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 call syntax_error(error, "Dependency "//list(idep)%key//" must be a table entry") exit end if call new_dependency(deps(idep), node, root, error) if (allocated(error)) exit end do end subroutine new_dependencies !> Write information on instance subroutine info(self, unit, verbosity) !> Instance of the dependency configuration class(dependency_config_t), intent(in) :: self !> Unit for IO integer, intent(in) :: unit !> Verbosity of the printout integer, intent(in), optional :: verbosity integer :: pr character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)' if (present(verbosity)) then pr = verbosity else pr = 1 end if write(unit, fmt) "Dependency" if (allocated(self%name)) then write(unit, fmt) "- name", self%name end if if (allocated(self%git)) then 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 end if end subroutine info !> Check if two dependency configurations are different logical function manifest_has_changed(this, that) result(has_changed) !> Two instances of the dependency configuration class(dependency_config_t), intent(in) :: this, that has_changed = .true. !> Perform all checks if (this%name/=that%name) return if (this%path/=that%path) return if (allocated(this%git).neqv.allocated(that%git)) return if (allocated(this%git)) then if (.not.(this%git==that%git)) return end if !> All checks passed! The two instances are equal has_changed = .false. end function manifest_has_changed end module fpm_manifest_dependency