diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 9e7493942a..13f2273ac4 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -92,6 +92,10 @@ pushd submodules "$fpm" build popd +pushd app_with_submodule +"$fpm" run --all +popd + pushd program_with_module "$fpm" build "$fpm" run --target Program_with_module @@ -118,6 +122,10 @@ pushd c_main "$fpm" run popd +pushd app_with_c +"$fpm" run +popd + pushd hello_fpm_path "$fpm" run popd diff --git a/example_packages/README.md b/example_packages/README.md index 2cf9fa09b8..c03cd42200 100644 --- a/example_packages/README.md +++ b/example_packages/README.md @@ -6,6 +6,8 @@ the features demonstrated in each package and which versions of fpm are supporte | Name | Features | Bootstrap (Haskell) fpm | fpm | |---------------------|---------------------------------------------------------------|:-----------------------:|:---:| +| app_with_c | C files located in app directory (not src) | N | Y | +| app_with_submodule | Submodules located in app directory (not src) | N | Y | | auto_discovery_off | Default layout with auto-discovery disabled | N | Y | | c_header_only | C header-only library | N | Y | | c_includes | C library with c include directory and dependency includes | N | Y | diff --git a/example_packages/app_with_c/.gitignore b/example_packages/app_with_c/.gitignore new file mode 100644 index 0000000000..a007feab07 --- /dev/null +++ b/example_packages/app_with_c/.gitignore @@ -0,0 +1 @@ +build/* diff --git a/example_packages/app_with_c/app/c_code.c b/example_packages/app_with_c/app/c_code.c new file mode 100644 index 0000000000..5a27c538b3 --- /dev/null +++ b/example_packages/app_with_c/app/c_code.c @@ -0,0 +1,11 @@ +#include +/* + * Decides whether a given file name is a directory. + * return 1 if file exists and is a directory + * Source (Public domain): https://github.com/urbanjost/M_system + */ +int my_isdir(const char *path) +{ + struct stat sb; + return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode); +} \ No newline at end of file diff --git a/example_packages/app_with_c/app/main.f90 b/example_packages/app_with_c/app/main.f90 new file mode 100644 index 0000000000..297352df15 --- /dev/null +++ b/example_packages/app_with_c/app/main.f90 @@ -0,0 +1,37 @@ +module with_c + use iso_c_binding, only: c_char, c_int, c_null_char + implicit none + +contains + + function system_isdir(dirname) + ! Source (Public domain): https://github.com/urbanjost/M_system + ! + implicit none + character(len=*), intent(in) :: dirname + logical :: system_isdir + + interface + function c_isdir(dirname) bind(C, name="my_isdir") result(c_ierr) + import c_char, c_int + character(kind=c_char, len=1), intent(in) :: dirname(*) + integer(kind=c_int) :: c_ierr + end function c_isdir + end interface + + system_isdir = c_isdir(trim(dirname)//c_null_char) == 1 + + end function system_isdir + +end module with_c + +program with_c_app + use with_c + implicit none + + write (*, *) "isdir('app') = ", system_isdir('app') + write (*, *) "isdir('src') = ", system_isdir('src') + write (*, *) "isdir('test') = ", system_isdir('test') + write (*, *) "isdir('bench') = ", system_isdir('bench') + +end program with_c_app diff --git a/example_packages/app_with_c/fpm.toml b/example_packages/app_with_c/fpm.toml new file mode 100644 index 0000000000..97e31109c5 --- /dev/null +++ b/example_packages/app_with_c/fpm.toml @@ -0,0 +1 @@ +name = "with_c" diff --git a/example_packages/app_with_submodule/.gitignore b/example_packages/app_with_submodule/.gitignore new file mode 100644 index 0000000000..a007feab07 --- /dev/null +++ b/example_packages/app_with_submodule/.gitignore @@ -0,0 +1 @@ +build/* diff --git a/example_packages/app_with_submodule/app/app1/child1.f90 b/example_packages/app_with_submodule/app/app1/child1.f90 new file mode 100644 index 0000000000..8f0c97247f --- /dev/null +++ b/example_packages/app_with_submodule/app/app1/child1.f90 @@ -0,0 +1,16 @@ +submodule(parent) child1 +implicit none + +interface + module function my_fun() result (b) + integer :: b + end function my_fun +end interface + +contains + +module procedure my_sub1 + a = my_fun() +end procedure my_sub1 + +end submodule child1 \ No newline at end of file diff --git a/example_packages/app_with_submodule/app/app1/grandchild.f90 b/example_packages/app_with_submodule/app/app1/grandchild.f90 new file mode 100644 index 0000000000..ad8913e641 --- /dev/null +++ b/example_packages/app_with_submodule/app/app1/grandchild.f90 @@ -0,0 +1,10 @@ +submodule(parent:child1) grandchild +implicit none + +contains + +module procedure my_fun + b = 1 +end procedure my_fun + +end submodule grandchild \ No newline at end of file diff --git a/example_packages/app_with_submodule/app/app1/main1.f90 b/example_packages/app_with_submodule/app/app1/main1.f90 new file mode 100644 index 0000000000..bbcc3450db --- /dev/null +++ b/example_packages/app_with_submodule/app/app1/main1.f90 @@ -0,0 +1,14 @@ +program test +use parent +implicit none + +integer :: a + +call my_sub1(a) + +if (a /= 1) then + write(*,*) 'FAILED: Unexpected value of a' + stop 1 +end if + +end program test \ No newline at end of file diff --git a/example_packages/app_with_submodule/app/app2/child2.f90 b/example_packages/app_with_submodule/app/app2/child2.f90 new file mode 100644 index 0000000000..9eb4fb9225 --- /dev/null +++ b/example_packages/app_with_submodule/app/app2/child2.f90 @@ -0,0 +1,10 @@ +submodule(parent) child2 +implicit none + +contains + +module procedure my_sub1 + a = 2 +end procedure my_sub1 + +end submodule child2 \ No newline at end of file diff --git a/example_packages/app_with_submodule/app/app2/main2.f90 b/example_packages/app_with_submodule/app/app2/main2.f90 new file mode 100644 index 0000000000..f528638900 --- /dev/null +++ b/example_packages/app_with_submodule/app/app2/main2.f90 @@ -0,0 +1,14 @@ +program test +use parent +implicit none + +integer :: a + +call my_sub1(a) + +if (a /= 2) then + write(*,*) 'FAILED: Unexpected value of a' + stop 1 +end if + +end program test \ No newline at end of file diff --git a/example_packages/app_with_submodule/fpm.toml b/example_packages/app_with_submodule/fpm.toml new file mode 100644 index 0000000000..8b2847ee18 --- /dev/null +++ b/example_packages/app_with_submodule/fpm.toml @@ -0,0 +1 @@ +name = "app_with_submodule" \ No newline at end of file diff --git a/example_packages/app_with_submodule/src/parent.f90 b/example_packages/app_with_submodule/src/parent.f90 new file mode 100644 index 0000000000..c3386eee65 --- /dev/null +++ b/example_packages/app_with_submodule/src/parent.f90 @@ -0,0 +1,11 @@ +module parent +implicit none + +interface + + module subroutine my_sub1(a) + integer, intent(out) :: a + end subroutine my_sub1 +end interface + +end module parent \ No newline at end of file diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90 index 8809795cf4..ca88bb5e35 100644 --- a/src/fpm_targets.f90 +++ b/src/fpm_targets.f90 @@ -148,6 +148,8 @@ subroutine targets_from_sources(targets,model,prune,error) call build_target_list(targets,model) + call collect_exe_link_dependencies(targets) + call resolve_module_dependencies(targets,model%external_modules,error) if (allocated(error)) return @@ -334,6 +336,57 @@ end function get_object_name end subroutine build_target_list +!> Add non-library non-module dependencies for executable targets +!> +!> Executable targets will link to any non-program non-module source files that +!> are in the same directory or in a subdirectory. +!> +!> (Note: Fortran module dependencies are handled separately in +!> `resolve_module_dependencies` and `resolve_target_linking`.) +!> +subroutine collect_exe_link_dependencies(targets) + type(build_target_ptr), intent(inout) :: targets(:) + + integer :: i, j + character(:), allocatable :: exe_source_dir + + ! Add non-module dependencies for executables + do j=1,size(targets) + + if (targets(j)%ptr%target_type == FPM_TARGET_EXECUTABLE) then + + do i=1,size(targets) + + if (i == j) cycle + + associate(exe => targets(j)%ptr, dep => targets(i)%ptr) + + exe_source_dir = dirname(exe%dependencies(1)%ptr%source%file_name) + + if (allocated(dep%source)) then + + if (dep%source%unit_scope /= FPM_SCOPE_LIB .and. & + dep%source%unit_type /= FPM_UNIT_PROGRAM .and. & + dep%source%unit_type /= FPM_UNIT_MODULE .and. & + index(dirname(dep%source%file_name), exe_source_dir) == 1) then + + call add_dependency(exe, dep) + + end if + + end if + + end associate + + end do + + end if + + end do + +end subroutine collect_exe_link_dependencies + + !> Allocate a new target and append to target list subroutine add_target(targets,package,type,output_name,source,link_libraries, macros, version) type(build_target_ptr), allocatable, intent(inout) :: targets(:)