Skip to content

[metapackage] stdlib: enable external BLAS/LAPACK #1139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ci/meta_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,10 @@ pushd metapackage_blas
"$fpm" run --verbose
popd

pushd metapackage_stdlib_extblas
"$fpm" build --verbose
"$fpm" run --verbose
popd

# Cleanup
rm -rf ./*/build
16 changes: 16 additions & 0 deletions example_packages/metapackage_stdlib_extblas/app/main.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
! fortran-lang stdlib + external BLAS test case
! Program will fail if an external BLAS has not been linked against.
program test_stdlib_metapackage
use stdlib_linalg_constants, only: external_blas_ilp32,external_lapack_ilp32, &
external_blas_ilp64,external_lapack_ilp64
implicit none

if (.not.(external_blas_ilp32 .or. external_blas_ilp64)) then
stop 1
elseif (.not.(external_lapack_ilp32 .or. external_lapack_ilp64)) then
stop 2
else
stop 0
end if

end program test_stdlib_metapackage
3 changes: 3 additions & 0 deletions example_packages/metapackage_stdlib_extblas/fpm.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = "test_stdlib_ext_blas"
dependencies.blas = "*"
dependencies.stdlib = "*"
4 changes: 4 additions & 0 deletions example_packages/preprocess_cpp/app/main.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
program cpp
use preprocess_cpp
call say_hello()
end program
66 changes: 37 additions & 29 deletions src/fpm.f90
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ module fpm
subroutine build_model(model, settings, package, error)
type(fpm_model_t), intent(out) :: model
class(fpm_build_settings), intent(inout) :: settings
type(package_config_t), intent(inout) :: package
type(package_config_t), intent(inout), target :: package
type(error_t), allocatable, intent(out) :: error

integer :: i, j
type(package_config_t) :: dependency
character(len=:), allocatable :: manifest, lib_dir
type(package_config_t), target :: dependency
type(package_config_t), pointer :: manifest
character(len=:), allocatable :: file_name, lib_dir
logical :: has_cpp
logical :: duplicates_found
type(string_t) :: include_dir
Expand Down Expand Up @@ -76,7 +77,7 @@ subroutine build_model(model, settings, package, error)
! Resolve meta-dependencies into the package and the model
call resolve_metapackages(model,package,settings,error)
if (allocated(error)) return

! Create dependencies
call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml"), &
& path_to_config=settings%path_to_config)
Expand All @@ -99,25 +100,32 @@ subroutine build_model(model, settings, package, error)
has_cpp = .false.
do i = 1, model%deps%ndep
associate(dep => model%deps%dep(i))
manifest = join_path(dep%proj_dir, "fpm.toml")

call get_package_data(dependency, manifest, error, apply_defaults=.true.)
if (allocated(error)) exit
file_name = join_path(dep%proj_dir, "fpm.toml")

model%packages(i)%name = dependency%name
! The main package manifest should not be reloaded, because it may have been
! affected by model dependencies and metapackages
if (i==1) then
manifest => package
else

call get_package_data(dependency, file_name, error, apply_defaults=.true.)
if (allocated(error)) exit

manifest => dependency
end if

model%packages(i)%name = manifest%name
associate(features => model%packages(i)%features)
features%implicit_typing = dependency%fortran%implicit_typing
features%implicit_external = dependency%fortran%implicit_external
features%source_form = dependency%fortran%source_form
features%implicit_typing = manifest%fortran%implicit_typing
features%implicit_external = manifest%fortran%implicit_external
features%source_form = manifest%fortran%source_form
end associate
model%packages(i)%version = package%version%s()

!> Add this dependency's manifest macros
call model%packages(i)%preprocess%destroy()

if (allocated(dependency%preprocess)) then
do j = 1, size(dependency%preprocess)
call model%packages(i)%preprocess%add_config(dependency%preprocess(j))
if (allocated(manifest%preprocess)) then
do j = 1, size(manifest%preprocess)
call model%packages(i)%preprocess%add_config(manifest%preprocess(j))
end do
end if

Expand All @@ -132,20 +140,20 @@ subroutine build_model(model, settings, package, error)

if (.not.allocated(model%packages(i)%sources)) allocate(model%packages(i)%sources(0))

if (allocated(dependency%library)) then
if (allocated(manifest%library)) then

if (allocated(dependency%library%source_dir)) then
lib_dir = join_path(dep%proj_dir, dependency%library%source_dir)
if (allocated(manifest%library%source_dir)) then
lib_dir = join_path(dep%proj_dir, manifest%library%source_dir)
if (is_dir(lib_dir)) then
call add_sources_from_dir(model%packages(i)%sources, lib_dir, FPM_SCOPE_LIB, &
with_f_ext=model%packages(i)%preprocess%suffixes, error=error)
if (allocated(error)) exit
end if
end if

if (allocated(dependency%library%include_dir)) then
do j=1,size(dependency%library%include_dir)
include_dir%s = join_path(dep%proj_dir, dependency%library%include_dir(j)%s)
if (allocated(manifest%library%include_dir)) then
do j=1,size(manifest%library%include_dir)
include_dir%s = join_path(dep%proj_dir, manifest%library%include_dir(j)%s)
if (is_dir(include_dir%s)) then
model%include_dirs = [model%include_dirs, include_dir]
end if
Expand All @@ -154,17 +162,17 @@ subroutine build_model(model, settings, package, error)

end if

if (allocated(dependency%build%link)) then
model%link_libraries = [model%link_libraries, dependency%build%link]
if (allocated(manifest%build%link)) then
model%link_libraries = [model%link_libraries, manifest%build%link]
end if

if (allocated(dependency%build%external_modules)) then
model%external_modules = [model%external_modules, dependency%build%external_modules]
if (allocated(manifest%build%external_modules)) then
model%external_modules = [model%external_modules, manifest%build%external_modules]
end if

! Copy naming conventions from this dependency's manifest
model%packages(i)%enforce_module_names = dependency%build%module_naming
model%packages(i)%module_prefix = dependency%build%module_prefix
model%packages(i)%enforce_module_names = manifest%build%module_naming
model%packages(i)%module_prefix = manifest%build%module_prefix

end associate
end do
Expand Down
44 changes: 44 additions & 0 deletions src/fpm/manifest/dependency.f90
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ module fpm_manifest_dependency

!> Print information on this instance
procedure :: info

!> Add a preprocessor configuration
procedure :: add_preprocess

!> Serialization interface
procedure :: serializable_is_same => dependency_is_same
Expand Down Expand Up @@ -561,6 +564,47 @@ pure subroutine resize_dependency_config(var, n)
end if

end subroutine resize_dependency_config

subroutine add_preprocess(dep, preprocess)
!> Instance of the dependency config
class(dependency_config_t), intent(inout) :: dep
!> Instance of the preprocessor configuration
type(preprocess_config_t), intent(in) :: preprocess

integer :: i,n
type(preprocess_config_t), allocatable :: new_preprocess(:)

if (allocated(dep%preprocess)) then

n = size(dep%preprocess)

if (n<1) then
deallocate(dep%preprocess)
allocate(dep%preprocess(1),source=preprocess)
else

find_similar: do i=1,n
if (dep%preprocess(i)%name==dep%name) then
call dep%preprocess(i)%add_config(preprocess)
return
end if
end do find_similar

! Similar preprocessor config not found: add a new one
allocate(new_preprocess(n+1))
new_preprocess(1:n) = dep%preprocess
new_preprocess(n+1) = preprocess
call move_alloc(from=new_preprocess,to=dep%preprocess)

end if
else

! Copy configuration
allocate(dep%preprocess(1),source=preprocess)

end if

end subroutine add_preprocess


end module fpm_manifest_dependency
63 changes: 62 additions & 1 deletion src/fpm/manifest/meta.f90
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ module fpm_manifest_metapackages

!> BLAS
type(metapackage_request_t) :: blas

contains

procedure :: get_requests

end type metapackage_config_t

Expand Down Expand Up @@ -217,7 +221,7 @@ subroutine new_meta_config(self, table, meta_allowed, error)
if (allocated(error)) return

end subroutine new_meta_config

!> Check local schema for allowed entries
logical function is_meta_package(key)

Expand All @@ -236,5 +240,62 @@ logical function is_meta_package(key)
end select

end function is_meta_package

!> Return a list of metapackages requested for the current build
function get_requests(meta) result(requests)

!> Instance of the build configuration
class(metapackage_config_t), intent(in) :: meta

!> The list of requested metapackages (always allocated)
type(metapackage_request_t), allocatable :: requests(:)

integer :: nreq

!> Count requests
nreq = 0
if (meta%mpi%on) nreq = nreq + 1
if (meta%openmp%on) nreq = nreq + 1
if (meta%stdlib%on) nreq = nreq + 1
if (meta%minpack%on) nreq = nreq + 1
if (meta%hdf5%on) nreq = nreq + 1
if (meta%netcdf%on) nreq = nreq + 1
if (meta%blas%on) nreq = nreq + 1

!> Prepare requests
allocate(requests(nreq)); if (nreq <= 0) return

nreq = 0
if (meta%mpi%on) then
nreq = nreq + 1
requests(nreq) = meta%mpi
end if
if (meta%openmp%on) then
nreq = nreq + 1
requests(nreq) = meta%openmp
end if
if (meta%stdlib%on) then
nreq = nreq + 1
requests(nreq) = meta%stdlib
end if
if (meta%minpack%on) then
nreq = nreq + 1
requests(nreq) = meta%minpack
end if
if (meta%hdf5%on) then
nreq = nreq + 1
requests(nreq) = meta%hdf5
end if
if (meta%netcdf%on) then
nreq = nreq + 1
requests(nreq) = meta%netcdf
end if
if (meta%blas%on) then
nreq = nreq + 1
requests(nreq) = meta%blas
end if

end function get_requests


end module fpm_manifest_metapackages
34 changes: 31 additions & 3 deletions src/fpm/manifest/preprocess.f90
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module fpm_manifest_preprocess
implicit none
private

public :: preprocess_config_t, new_preprocess_config, new_preprocessors, operator(==)
public :: preprocess_config_t, new_preprocessors, operator(==)

!> Configuration meta data for a preprocessor
type, extends(serializable_t) :: preprocess_config_t
Expand All @@ -41,6 +41,11 @@ module fpm_manifest_preprocess

!> Print information on this instance
procedure :: info

!> Initialization
procedure, private :: new_cpp_config_with_macros
procedure, private :: new_preprocess_config
generic :: new => new_cpp_config_with_macros, new_preprocess_config

!> Serialization interface
procedure :: serializable_is_same => preprocess_is_same
Expand All @@ -61,11 +66,34 @@ module fpm_manifest_preprocess

contains

!> Construct a new cpp preprocessor configuration with a list of macros
subroutine new_cpp_config_with_macros(self, macros)

!> Instance of the preprocess configuration
class(preprocess_config_t), intent(out) :: self

!> List of macros
type(string_t), intent(in) :: macros(:)

call self%destroy()

!> Set cpp
self%name = "cpp"

!> Set macros
if (size(macros)<=0) then
allocate(self%macros(0))
else
allocate(self%macros, source=macros)
end if

end subroutine new_cpp_config_with_macros

!> Construct a new preprocess configuration from TOML data structure
subroutine new_preprocess_config(self, table, error)

!> Instance of the preprocess configuration
type(preprocess_config_t), intent(out) :: self
class(preprocess_config_t), intent(out) :: self

!> Instance of the TOML data structure.
type(toml_table), intent(inout) :: table
Expand Down Expand Up @@ -145,7 +173,7 @@ subroutine new_preprocessors(preprocessors, table, error)
call syntax_error(error, "Preprocessor "//list(iprep)%key//" must be a table entry")
exit
end if
call new_preprocess_config(preprocessors(iprep), node, error)
call preprocessors(iprep)%new(node, error)
if (allocated(error)) exit
end do

Expand Down
Loading
Loading