build_model Subroutine

public subroutine build_model(model, settings, package_config, error)

Constructs a valid fpm model from command line settings and the toml manifest. Add this dependency’s manifest macros Add this dependency’s package-level macros

Arguments

Type IntentOptional Attributes Name
type(fpm_model_t), intent(out) :: model
class(fpm_build_settings), intent(inout) :: settings
type(package_config_t), intent(inout), target :: package_config
type(error_t), intent(out), allocatable :: error

Source Code

subroutine build_model(model, settings, package_config, error)
    type(fpm_model_t), intent(out) :: model
    class(fpm_build_settings), intent(inout) :: settings
    type(package_config_t), intent(inout), target :: package_config
    type(error_t), allocatable, intent(out) :: error

    integer :: i, j
    type(package_config_t), target  :: package, dependency_config, dependency
    type(package_config_t), pointer :: manifest
    type(platform_config_t) :: target_platform
    character(len=:), allocatable :: file_name, lib_dir
    logical :: has_cpp
    logical :: duplicates_found, auto_exe, auto_example, auto_test
    type(string_t) :: include_dir

    model%package_name = package_config%name

    ! Set target OS to current OS (may be extended for cross-compilation in the future)
    model%target_os = get_os_type()

    allocate(model%include_dirs(0))
    allocate(model%link_libraries(0))
    allocate(model%external_modules(0))
    
    call new_compiler(model%compiler, settings%compiler, settings%c_compiler, &
        & settings%cxx_compiler, echo=settings%verbose, verbose=settings%verbose)
    call new_archiver(model%archiver, settings%archiver, &
        & echo=settings%verbose, verbose=settings%verbose)

    if (model%compiler%is_unknown()) then
        write(*, '(*(a:,1x))') &
            "<WARN>", "Unknown compiler", model%compiler%fc, "requested!", &
            "Defaults for this compiler might be incorrect"
    end if
    
    ! Extract the target platform for this build
    target_platform = model%target_platform()
    
    call new_compiler_flags(model,settings)
    model%build_dir            = settings%build_dir
    model%build_prefix         = join_path(settings%build_dir, basename(model%compiler%fc))
    model%include_tests        = settings%build_tests    
    
    ! Extract the current package configuration request
    package = package_config%export_config(target_platform)    
    
    ! Resolve meta-dependencies into the package and the model
    call resolve_metapackages(model,package,settings,error)
    if (allocated(error)) return
    
    if (allocated(package%build)) then 
        model%enforce_module_names = package%build%module_naming
        model%module_prefix        = package%build%module_prefix
    endif      
    
    ! Create dependencies
    call new_dependency_tree(model%deps, cache=join_path(settings%build_dir, "cache.toml"), &
    & path_to_config=settings%path_to_config, build_dir=settings%build_dir)

    ! Build and resolve model dependencies
    call model%deps%add(package, error)
    if (allocated(error)) return

    ! Update dependencies where needed
    call model%deps%update(error)
    if (allocated(error)) return

    ! build directory should now exist
    if (.not.exists(join_path(settings%build_dir, ".gitignore"))) then
      call filewrite(join_path(settings%build_dir, ".gitignore"),["*"])
    end if

    allocate(model%packages(model%deps%ndep))
    has_cpp = .false.

    do i = 1, model%deps%ndep
        associate(dep => model%deps%dep(i))
            file_name = join_path(dep%proj_dir, "fpm.toml")

            ! 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
                
                ! Extract this dependency config
                call get_package_data(dependency_config, file_name, error, apply_defaults=.true.)
                if (allocated(error)) exit          
                
                ! Adapt it to the current profile/platform
                dependency = dependency_config%export_config(target_platform)
                
                manifest => dependency
            end if            
            

            model%packages(i)%name     = manifest%name
            model%packages(i)%features = manifest%fortran
            model%packages(i)%version  = manifest%version

            !> Add this dependency's manifest macros
            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

            !> Add this dependency's package-level macros
            if (allocated(dep%preprocess)) then
                do j = 1, size(dep%preprocess)
                    call model%packages(i)%preprocess%add_config(dep%preprocess(j))
                end do
            end if

            if (model%packages(i)%preprocess%is_cpp()) has_cpp = .true.

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

            if (allocated(manifest%library)) then

                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, &
                            preprocess=model%packages(i)%preprocess)
                        if (allocated(error)) exit
                    end if
                end if

                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
                    end do
                end if

            end if
            
            if (allocated(manifest%build)) then 

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

                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 = manifest%build%module_naming
                model%packages(i)%module_prefix        = manifest%build%module_prefix
            
            endif

        end associate
    end do
    if (allocated(error)) return

    ! Add optional flags
    if (has_cpp) call set_cpp_preprocessor_flags(model%compiler%id, model%fortran_compile_flags)

    ! Add sources from executable directories
    
    if (allocated(package%build)) then 
        auto_exe = package%build%auto_executables
        auto_example = package%build%auto_examples
        auto_test = package%build%auto_tests
    else
        auto_exe = .true.
        auto_example = .true.
        auto_test = .true.
    endif
    
    if (is_dir('app') .and. auto_exe) then
        call add_sources_from_dir(model%packages(1)%sources,'app', FPM_SCOPE_APP, &
                                   with_executables=.true., with_f_ext=model%packages(1)%preprocess%suffixes,&
                                   error=error,preprocess=model%packages(1)%preprocess)

        if (allocated(error)) then
            return
        end if

    end if
    if (is_dir('example') .and. auto_example) then
        call add_sources_from_dir(model%packages(1)%sources,'example', FPM_SCOPE_EXAMPLE, &
                                  with_executables=.true., &
                                  with_f_ext=model%packages(1)%preprocess%suffixes,error=error,&
                                  preprocess=model%packages(1)%preprocess)

        if (allocated(error)) then
            return
        end if

    end if
    if (is_dir('test') .and. auto_test) then
        call add_sources_from_dir(model%packages(1)%sources,'test', FPM_SCOPE_TEST, &
                                  with_executables=.true., &
                                  with_f_ext=model%packages(1)%preprocess%suffixes,error=error,&
                                  preprocess=model%packages(1)%preprocess)

        if (allocated(error)) then
            return
        endif

    end if
    if (allocated(package%executable)) then
        call add_executable_sources(model%packages(1)%sources, package%executable, FPM_SCOPE_APP, &
                                     auto_discover=auto_exe, &
                                     with_f_ext=model%packages(1)%preprocess%suffixes, &
                                     error=error,preprocess=model%packages(1)%preprocess)

        if (allocated(error)) then
            return
        end if

    end if
    if (allocated(package%example)) then
        call add_executable_sources(model%packages(1)%sources, package%example, FPM_SCOPE_EXAMPLE, &
                                     auto_discover=auto_example, &
                                     with_f_ext=model%packages(1)%preprocess%suffixes, &
                                     error=error,preprocess=model%packages(1)%preprocess)

        if (allocated(error)) then
            return
        end if

    end if
    if (allocated(package%test)) then
        call add_executable_sources(model%packages(1)%sources, package%test, FPM_SCOPE_TEST, &
                                     auto_discover=auto_test, &
                                     with_f_ext=model%packages(1)%preprocess%suffixes, &
                                     error=error,preprocess=model%packages(1)%preprocess)

        if (allocated(error)) then
            return
        endif

    endif

    if (settings%verbose) then
        write(*,*)'<INFO> BUILD_NAME: ',model%build_prefix
        write(*,*)'<INFO> COMPILER:  ',model%compiler%fc
        write(*,*)'<INFO> C COMPILER:  ',model%compiler%cc
        write(*,*)'<INFO> CXX COMPILER: ',model%compiler%cxx
        write(*,*)'<INFO> COMPILER OPTIONS:  ', model%fortran_compile_flags
        write(*,*)'<INFO> C COMPILER OPTIONS:  ', model%c_compile_flags
        write(*,*)'<INFO> CXX COMPILER OPTIONS: ', model%cxx_compile_flags
        write(*,*)'<INFO> LINKER OPTIONS:  ', model%link_flags
        write(*,*)'<INFO> INCLUDE DIRECTORIES:  [', string_cat(model%include_dirs,','),']'
    end if

    ! Check for invalid module names
    call check_module_names(model, error)
    if (allocated(error)) return

    ! Check for duplicate modules
    duplicates_found = .false.
    call check_modules_for_duplicates(model, duplicates_found)
    if (duplicates_found) then
        call fpm_stop(1,'*build_model*:Error: One or more duplicate module names found.')
    end if
end subroutine build_model