Skip to content

Implement fpm-install command #257

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 10 commits into from
Dec 18, 2020
6 changes: 6 additions & 0 deletions ci/run_tests.bat
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ echo %fpm_path%
%fpm_path%
if errorlevel 1 exit 1

%fpm_path% build
if errorlevel 1 exit 1

%fpm_path% install --prefix "%CD%\_dist" --no-rebuild
if errorlevel 1 exit 1

cd ..\example_packages\hello_world
if errorlevel 1 exit 1

Expand Down
10 changes: 9 additions & 1 deletion ci/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@ rm -rf fpm_scratch_*/
fpm test $@
rm -rf fpm_scratch_*/

# Build example packages
f_fpm_path="$(fpm run $@ --runner echo)"

# Let fpm build itself
"${f_fpm_path}" build

# Install fpm into local directory
"${f_fpm_path}" install --prefix "$PWD/_dist" --no-rebuild

# Build example packages
cd ../example_packages/
rm -rf ./*/build

cd hello_world

"${f_fpm_path}" build
./build/gfortran_debug/app/hello_world
"${f_fpm_path}" run
Expand Down
3 changes: 2 additions & 1 deletion fpm/app/main.f90
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ program main
fpm_install_settings, &
fpm_update_settings, &
get_command_line_settings
use fpm, only: cmd_build, cmd_install, cmd_run
use fpm, only: cmd_build, cmd_run
use fpm_cmd_install, only: cmd_install
use fpm_cmd_new, only: cmd_new
use fpm_cmd_update, only : cmd_update

Expand Down
9 changes: 2 additions & 7 deletions fpm/src/fpm.f90
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ module fpm
use fpm_manifest_dependency, only: dependency_config_t
implicit none
private
public :: build_model, cmd_build, cmd_install, cmd_run
public :: cmd_build, cmd_run
public :: build_model

contains

Expand Down Expand Up @@ -204,12 +205,6 @@ subroutine cmd_build(settings)

end subroutine

subroutine cmd_install(settings)
type(fpm_install_settings), intent(in) :: settings
print *, "fpm error: 'fpm install' not implemented."
error stop 8
end subroutine cmd_install

subroutine cmd_run(settings,test)
class(fpm_run_settings), intent(in) :: settings
logical, intent(in) :: test
Expand Down
170 changes: 170 additions & 0 deletions fpm/src/fpm/cmd/install.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
module fpm_cmd_install
use, intrinsic :: iso_fortran_env, only : output_unit
use fpm, only : build_model
use fpm_backend, only : build_package
use fpm_command_line, only : fpm_install_settings
use fpm_error, only : error_t, fatal_error
use fpm_filesystem, only : join_path, list_files
use fpm_installer, only : installer_t, new_installer
use fpm_manifest, only : package_config_t, get_package_data
use fpm_model, only : fpm_model_t, build_target_t, FPM_TARGET_EXECUTABLE, &
FPM_SCOPE_APP
use fpm_strings, only : string_t, resize
implicit none
private

public :: cmd_install

contains

!> Entry point for the fpm-install subcommand
subroutine cmd_install(settings)
!> Representation of the command line settings
type(fpm_install_settings), intent(in) :: settings
type(package_config_t) :: package
type(error_t), allocatable :: error
type(fpm_model_t) :: model
type(installer_t) :: installer
character(len=:), allocatable :: lib, exe, dir
logical :: installable

call get_package_data(package, "fpm.toml", error, apply_defaults=.true.)
call handle_error(error)

call build_model(model, settings%fpm_build_settings, package, error)
call handle_error(error)

installable = (allocated(package%library) .and. package%install%library) &
.or. allocated(package%executable)
if (.not.installable) then
call fatal_error(error, "Project does not contain any installable targets")
call handle_error(error)
end if

if (settings%list) then
call install_info(output_unit, package, model)
return
end if

if (.not.settings%no_rebuild) then
call build_package(model)
end if

call new_installer(installer, prefix=settings%prefix, &
bindir=settings%bindir, libdir=settings%libdir, &
includedir=settings%includedir, &
verbosity=merge(2, 1, settings%verbose))

if (allocated(package%library) .and. package%install%library) then
dir = join_path(model%output_directory, model%package_name)
lib = "lib"//model%package_name//".a"
call installer%install_library(join_path(dir, lib), error)
call handle_error(error)

call install_module_files(installer, dir, error)
call handle_error(error)
end if

if (allocated(package%executable)) then
call install_executables(installer, model, error)
call handle_error(error)
end if

end subroutine cmd_install

subroutine install_info(unit, package, model)
integer, intent(in) :: unit
type(package_config_t), intent(in) :: package
type(fpm_model_t), intent(in) :: model

integer :: ii, ntargets
character(len=:), allocatable :: lib
type(string_t), allocatable :: install_target(:)

call resize(install_target)

ntargets = 0
if (allocated(package%library) .and. package%install%library) then
ntargets = ntargets + 1
lib = join_path(model%output_directory, model%package_name, &
"lib"//model%package_name//".a")
install_target(ntargets)%s = lib
end if
do ii = 1, size(model%targets)
if (is_executable_target(model%targets(ii)%ptr)) then
if (ntargets >= size(install_target)) call resize(install_target)
ntargets = ntargets + 1
install_target(ntargets)%s = model%targets(ii)%ptr%output_file
end if
end do

write(unit, '("#", *(1x, g0))') &
"total number of installable targets:", ntargets
do ii = 1, ntargets
write(unit, '("-", *(1x, g0))') install_target(ii)%s
end do

end subroutine install_info

subroutine install_module_files(installer, dir, error)
type(installer_t), intent(inout) :: installer
character(len=*), intent(in) :: dir
type(error_t), allocatable, intent(out) :: error
type(string_t), allocatable :: modules(:)
integer :: ii

call list_files(dir, modules, recurse=.false.)

do ii = 1, size(modules)
if (is_module_file(modules(ii)%s)) then
call installer%install_header(modules(ii)%s, error)
if (allocated(error)) exit
end if
end do
if (allocated(error)) return

end subroutine install_module_files

subroutine install_executables(installer, model, error)
type(installer_t), intent(inout) :: installer
type(fpm_model_t), intent(in) :: model
type(error_t), allocatable, intent(out) :: error
integer :: ii

do ii = 1, size(model%targets)
if (is_executable_target(model%targets(ii)%ptr)) then
call installer%install_executable(model%targets(ii)%ptr%output_file, error)
if (allocated(error)) exit
end if
end do
if (allocated(error)) return

end subroutine install_executables

elemental function is_executable_target(target_ptr) result(is_exe)
type(build_target_t), intent(in) :: target_ptr
logical :: is_exe
is_exe = target_ptr%target_type == FPM_TARGET_EXECUTABLE .and. &
allocated(target_ptr%dependencies)
if (is_exe) then
is_exe = target_ptr%dependencies(1)%ptr%source%unit_scope == FPM_SCOPE_APP
end if
end function is_executable_target

elemental function is_module_file(name) result(is_mod)
character(len=*), intent(in) :: name
logical :: is_mod
integer :: ll
ll = len(name)
is_mod = name(max(1, ll-3):ll) == ".mod"
end function is_module_file

subroutine handle_error(error)
type(error_t), intent(in), optional :: error
if (present(error)) then
print '("[Error]", 1x, a)', error%message
error stop 1
end if
end subroutine handle_error

end module fpm_cmd_install
Loading