aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fpm.f9028
-rw-r--r--src/fpm/cmd/new.f902
-rw-r--r--src/fpm/manifest/build.f9016
-rw-r--r--src/fpm_backend.f9076
-rw-r--r--src/fpm_command_line.f9034
-rw-r--r--src/fpm_compiler.f9035
-rw-r--r--src/fpm_environment.f9036
-rw-r--r--src/fpm_filesystem.f9021
-rw-r--r--src/fpm_model.f9020
-rw-r--r--src/fpm_os.F90105
-rw-r--r--src/fpm_targets.f9032
11 files changed, 332 insertions, 73 deletions
diff --git a/src/fpm.f90 b/src/fpm.f90
index 31b68ff..5854cfb 100644
--- a/src/fpm.f90
+++ b/src/fpm.f90
@@ -4,12 +4,12 @@ use fpm_backend, only: build_package
use fpm_command_line, only: fpm_build_settings, fpm_new_settings, &
fpm_run_settings, fpm_install_settings, fpm_test_settings
use fpm_dependency, only : new_dependency_tree
-use fpm_environment, only: run
+use fpm_environment, only: run, get_env, get_archiver
use fpm_filesystem, only: is_dir, join_path, number_of_rows, list_files, exists, basename
use fpm_model, only: fpm_model_t, srcfile_t, show_model, &
FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, FPM_SCOPE_DEP, &
FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST
-use fpm_compiler, only: get_module_flags, is_unknown_compiler
+use fpm_compiler, only: get_module_flags, is_unknown_compiler, get_default_c_compiler
use fpm_sources, only: add_executable_sources, add_sources_from_dir
@@ -51,6 +51,7 @@ subroutine build_model(model, settings, package, error)
allocate(model%include_dirs(0))
allocate(model%link_libraries(0))
+ allocate(model%external_modules(0))
call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml"))
call model%deps%add(package, error)
@@ -62,6 +63,10 @@ subroutine build_model(model, settings, package, error)
model%fortran_compiler = settings%compiler
endif
+ model%archiver = get_archiver()
+ call get_default_c_compiler(model%fortran_compiler, model%c_compiler)
+ model%c_compiler = get_env('FPM_C_COMPILER',model%c_compiler)
+
if (is_unknown_compiler(model%fortran_compiler)) then
write(*, '(*(a:,1x))') &
"<WARN>", "Unknown compiler", model%fortran_compiler, "requested!", &
@@ -147,7 +152,7 @@ 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(dependency%library%source_dir)) then
lib_dir = join_path(dep%proj_dir, dependency%library%source_dir)
if (is_dir(lib_dir)) then
@@ -165,12 +170,16 @@ subroutine build_model(model, settings, package, error)
end if
end do
end if
-
+
end if
if (allocated(dependency%build%link)) then
model%link_libraries = [model%link_libraries, dependency%build%link]
end if
+
+ if (allocated(dependency%build%external_modules)) then
+ model%external_modules = [model%external_modules, dependency%build%external_modules]
+ end if
end associate
end do
if (allocated(error)) return
@@ -178,8 +187,9 @@ subroutine build_model(model, settings, package, error)
if (settings%verbose) then
write(*,*)'<INFO> BUILD_NAME: ',settings%build_name
write(*,*)'<INFO> COMPILER: ',settings%compiler
- write(*,*)'<INFO> COMPILER OPTIONS: ', model%fortran_compile_flags
- write(*,*)'<INFO> INCLUDE DIRECTORIES: [', string_cat(model%include_dirs,','),']'
+ write(*,*)'<INFO> C COMPILER: ',model%c_compiler
+ write(*,*)'<INFO> COMPILER OPTIONS: ', model%fortran_compile_flags
+ write(*,*)'<INFO> INCLUDE DIRECTORIES: [', string_cat(model%include_dirs,','),']'
end if
! Check for duplicate modules
@@ -190,7 +200,7 @@ subroutine build_model(model, settings, package, error)
end subroutine build_model
! Check for duplicate modules
-subroutine check_modules_for_duplicates(model, duplicates_found)
+subroutine check_modules_for_duplicates(model, duplicates_found)
type(fpm_model_t), intent(in) :: model
integer :: maxsize
integer :: i,j,k,l,m,modi
@@ -370,7 +380,7 @@ subroutine cmd_run(settings,test)
! Check all names are valid
! or no name and found more than one file
- toomany= size(settings%name).eq.0 .and. size(executables).gt.1
+ toomany= size(settings%name).eq.0 .and. size(executables).gt.1
if ( any(.not.found) &
& .or. &
& ( (toomany .and. .not.test) .or. (toomany .and. settings%runner .ne. '') ) &
@@ -420,7 +430,7 @@ subroutine cmd_run(settings,test)
end if
end do
endif
- contains
+ contains
subroutine compact_list_all()
integer, parameter :: LINE_WIDTH = 80
integer :: i, j, nCol
diff --git a/src/fpm/cmd/new.f90 b/src/fpm/cmd/new.f90
index 5149bea..773d7a7 100644
--- a/src/fpm/cmd/new.f90
+++ b/src/fpm/cmd/new.f90
@@ -347,7 +347,7 @@ character(len=:,kind=tfc),allocatable :: littlefile(:)
&' # git repository. ',&
&' # ',&
&' # You can be specific about which version of a dependency you would ',&
- &' # like. By default the latest master master branch is used. You can ',&
+ &' # like. By default the latest default branch is used. You can ',&
&' # optionally specify a branch, a tag or a commit value. ',&
&' # ',&
&' # So here are several alternates for specifying a remote dependency (you ',&
diff --git a/src/fpm/manifest/build.f90 b/src/fpm/manifest/build.f90
index d96974f..c9b3f44 100644
--- a/src/fpm/manifest/build.f90
+++ b/src/fpm/manifest/build.f90
@@ -34,6 +34,9 @@ module fpm_manifest_build
!> Libraries to link against
type(string_t), allocatable :: link(:)
+ !> External modules to use
+ type(string_t), allocatable :: external_modules(:)
+
contains
!> Print information on this instance
@@ -87,6 +90,9 @@ contains
call get_value(table, "link", self%link, error)
if (allocated(error)) return
+ call get_value(table, "external-modules", self%external_modules, error)
+ if (allocated(error)) return
+
end subroutine new_build_config
@@ -110,7 +116,7 @@ contains
do ikey = 1, size(list)
select case(list(ikey)%key)
- case("auto-executables", "auto-examples", "auto-tests", "link")
+ case("auto-executables", "auto-examples", "auto-tests", "link", "external-modules")
continue
case default
@@ -135,7 +141,7 @@ contains
!> Verbosity of the printout
integer, intent(in), optional :: verbosity
- integer :: pr, ilink
+ integer :: pr, ilink, imod
character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)'
if (present(verbosity)) then
@@ -156,6 +162,12 @@ contains
write(unit, fmt) " - " // self%link(ilink)%s
end do
end if
+ if (allocated(self%external_modules)) then
+ write(unit, fmt) " - external modules"
+ do imod = 1, size(self%external_modules)
+ write(unit, fmt) " - " // self%external_modules(imod)%s
+ end do
+ end if
end subroutine info
diff --git a/src/fpm_backend.f90 b/src/fpm_backend.f90
index 799b7a6..bdec3af 100644
--- a/src/fpm_backend.f90
+++ b/src/fpm_backend.f90
@@ -1,39 +1,38 @@
!># Build backend
-!> Uses a list of `[[build_target_ptr]]` and a valid `[[fpm_model]]` instance
+!> Uses a list of `[[build_target_ptr]]` and a valid `[[fpm_model]]` instance
!> to schedule and execute the compilation and linking of package targets.
-!>
+!>
!> The package build process (`[[build_package]]`) comprises three steps:
!>
!> 1. __Target sorting:__ topological sort of the target dependency graph (`[[sort_target]]`)
!> 2. __Target scheduling:__ group targets into schedule regions based on the sorting (`[[schedule_targets]]`)
!> 3. __Target building:__ generate targets by compilation or linking
-!>
+!>
!> @note If compiled with OpenMP, targets will be build in parallel where possible.
!>
!>### Incremental compilation
-!> The backend process supports *incremental* compilation whereby targets are not
+!> The backend process supports *incremental* compilation whereby targets are not
!> re-compiled if their corresponding dependencies have not been modified.
-!>
+!>
!> - Source-based targets (*i.e.* objects) are not re-compiled if the corresponding source
!> file is unmodified AND all of the target dependencies are not marked for re-compilation
!>
-!> - Link targets (*i.e.* executables and libraries) are not re-compiled if the
+!> - Link targets (*i.e.* executables and libraries) are not re-compiled if the
!> target output file already exists AND all of the target dependencies are not marked for
!> re-compilation
!>
!> Source file modification is determined by a file digest (hash) which is calculated during
-!> the source parsing phase ([[fpm_source_parsing]]) and cached to disk after a target is
+!> the source parsing phase ([[fpm_source_parsing]]) and cached to disk after a target is
!> successfully generated.
!>
module fpm_backend
-use fpm_environment, only: run
-use fpm_filesystem, only: basename, dirname, join_path, exists, mkdir
+use fpm_environment, only: run, get_os_type, OS_WINDOWS
+use fpm_filesystem, only: basename, dirname, join_path, exists, mkdir, unix_path
use fpm_model, only: fpm_model_t
-use fpm_targets, only: build_target_t, build_target_ptr, &
- FPM_TARGET_OBJECT, FPM_TARGET_ARCHIVE, FPM_TARGET_EXECUTABLE
-
-use fpm_strings, only: string_cat
+use fpm_targets, only: build_target_t, build_target_ptr, FPM_TARGET_OBJECT, &
+ FPM_TARGET_C_OBJECT, FPM_TARGET_ARCHIVE, FPM_TARGET_EXECUTABLE
+use fpm_strings, only: string_cat, string_t
implicit none
@@ -59,9 +58,9 @@ subroutine build_package(targets,model)
! Perform depth-first topological sort of targets
do i=1,size(targets)
-
+
call sort_target(targets(i)%ptr)
-
+
end do
! Construct build schedule queue
@@ -106,20 +105,20 @@ subroutine build_package(targets,model)
end if
end do
-
+
end subroutine build_package
-!> Topologically sort a target for scheduling by
+!> Topologically sort a target for scheduling by
!> recursing over its dependencies.
-!>
+!>
!> Checks disk-cached source hashes to determine if objects are
!> up-to-date. Up-to-date sources are tagged as skipped.
!>
-!> On completion, `target` should either be marked as
+!> On completion, `target` should either be marked as
!> sorted (`target%sorted=.true.`) or skipped (`target%skip=.true.`)
!>
-!> If `target` is marked as sorted, `target%schedule` should be an
+!> If `target` is marked as sorted, `target%schedule` should be an
!> integer greater than zero indicating the region for scheduling
!>
recursive subroutine sort_target(target)
@@ -190,7 +189,7 @@ recursive subroutine sort_target(target)
end if
end do
-
+
! Mark flag as processed: either sorted or skipped
target%sorted = .not.target%skip
@@ -270,14 +269,28 @@ subroutine build_target(model,target,stat)
call run(model%fortran_compiler//" -c " // target%source%file_name // target%compile_flags &
// " -o " // target%output_file, echo=.true., exitstat=stat)
+ case (FPM_TARGET_C_OBJECT)
+ call run(model%c_compiler//" -c " // target%source%file_name // target%compile_flags &
+ // " -o " // target%output_file)
+
case (FPM_TARGET_EXECUTABLE)
-
+
call run(model%fortran_compiler// " " // target%compile_flags &
//" "//target%link_flags// " -o " // target%output_file, echo=.true., exitstat=stat)
case (FPM_TARGET_ARCHIVE)
- call run("ar -rs " // target%output_file // " " // string_cat(target%link_objects," "), &
- echo=.true., exitstat=stat)
+
+ select case (get_os_type())
+ case (OS_WINDOWS)
+ call write_response_file(target%output_file//".resp" ,target%link_objects)
+ call run(model%archiver // target%output_file // " @" // target%output_file//".resp", &
+ echo=.true., exitstat=stat)
+
+ case default
+ call run(model%archiver // target%output_file // " " // string_cat(target%link_objects," "), &
+ echo=.true., exitstat=stat)
+
+ end select
end select
@@ -289,4 +302,19 @@ subroutine build_target(model,target,stat)
end subroutine build_target
+!> Response files allow to read command line options from files.
+!> Whitespace is used to separate the arguments, we will use newlines
+!> as separator to create readable response files which can be inspected
+!> in case of errors.
+subroutine write_response_file(name, argv)
+ character(len=*), intent(in) :: name
+ type(string_t), intent(in) :: argv(:)
+ integer :: iarg, io
+ open(file=name, newunit=io)
+ do iarg = 1, size(argv)
+ write(io, '(a)') unix_path(argv(iarg)%s)
+ end do
+ close(io)
+end subroutine write_response_file
+
end module fpm_backend
diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90
index 9e9a572..f44bcd0 100644
--- a/src/fpm_command_line.f90
+++ b/src/fpm_command_line.f90
@@ -25,7 +25,7 @@
module fpm_command_line
use fpm_environment, only : get_os_type, get_env, &
OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, &
- OS_CYGWIN, OS_SOLARIS, OS_FREEBSD
+ OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD
use M_CLI2, only : set_args, lget, sget, unnamed, remaining, specified
use fpm_strings, only : lower, split, fnv_1a
use fpm_filesystem, only : basename, canon_path, to_fortran_name
@@ -46,6 +46,7 @@ public :: fpm_cmd_settings, &
get_command_line_settings
type, abstract :: fpm_cmd_settings
+ character(len=:), allocatable :: working_dir
logical :: verbose=.true.
end type
@@ -119,6 +120,7 @@ contains
integer :: i
integer :: widest
type(fpm_install_settings), allocatable :: install_settings
+ character(len=:), allocatable :: common_args, working_dir
call set_help()
! text for --version switch,
@@ -129,6 +131,7 @@ contains
case (OS_CYGWIN); os_type = "OS Type: Cygwin"
case (OS_SOLARIS); os_type = "OS Type: Solaris"
case (OS_FREEBSD); os_type = "OS Type: FreeBSD"
+ case (OS_OPENBSD); os_type = "OS Type: OpenBSD"
case (OS_UNKNOWN); os_type = "OS Type: Unknown"
case default ; os_type = "OS Type: UNKNOWN"
end select
@@ -147,12 +150,14 @@ contains
if(adjustl(cmdarg(1:1)) .ne. '-')exit
enddo
+ common_args = '--directory:C " " '
+
! now set subcommand-specific help text and process commandline
! arguments. Then call subcommand routine
select case(trim(cmdarg))
case('run')
- call set_args('&
+ call set_args(common_args //'&
& --target " " &
& --list F &
& --all F &
@@ -205,7 +210,7 @@ contains
& verbose=lget('verbose') )
case('build')
- call set_args( '&
+ call set_args(common_args // '&
& --profile " " &
& --list F &
& --show-model F &
@@ -227,7 +232,7 @@ contains
& verbose=lget('verbose') )
case('new')
- call set_args('&
+ call set_args(common_args // '&
& --src F &
& --lib F &
& --app F &
@@ -297,7 +302,7 @@ contains
endif
case('help','manual')
- call set_args('&
+ call set_args(common_args // '&
& --verbose F &
& ',help_help,version_text)
if(size(unnamed).lt.2)then
@@ -345,7 +350,8 @@ contains
call printhelp(help_text)
case('install')
- call set_args('--profile " " --no-rebuild F --verbose F --prefix " " &
+ call set_args(common_args // '&
+ & --profile " " --no-rebuild F --verbose F --prefix " " &
& --list F &
& --compiler "'//get_env('FPM_COMPILER','gfortran')//'" &
& --flag:: " "&
@@ -370,7 +376,7 @@ contains
call move_alloc(install_settings, cmd_settings)
case('list')
- call set_args('&
+ call set_args(common_args // '&
& --list F&
& --verbose F&
&', help_list, version_text)
@@ -379,7 +385,7 @@ contains
call printhelp(help_list_dash)
endif
case('test')
- call set_args('&
+ call set_args(common_args // '&
& --target " " &
& --list F&
& --profile " "&
@@ -424,7 +430,7 @@ contains
& verbose=lget('verbose') )
case('update')
- call set_args('--fetch-only F --verbose F --clean F', &
+ call set_args(common_args // ' --fetch-only F --verbose F --clean F', &
help_update, version_text)
if( size(unnamed) .gt. 1 )then
@@ -440,7 +446,7 @@ contains
case default
- call set_args('&
+ call set_args(common_args // '&
& --list F&
& --verbose F&
&', help_fpm, version_text)
@@ -461,6 +467,12 @@ contains
call printhelp(help_text)
end select
+
+ if (allocated(cmd_settings)) then
+ working_dir = sget("directory")
+ call move_alloc(working_dir, cmd_settings%working_dir)
+ end if
+
contains
subroutine check_build_vals()
@@ -673,6 +685,8 @@ contains
' install [--profile PROF] [--flag FFLAGS] [--no-rebuild] [--prefix PATH] [options]', &
' ', &
'SUBCOMMAND OPTIONS ', &
+ ' -C, --directory PATH', &
+ ' Change working directory to PATH before running any command', &
' --profile PROF selects the compilation profile for the build.',&
' Currently available profiles are "release" for',&
' high optimization and "debug" for full debug options.',&
diff --git a/src/fpm_compiler.f90 b/src/fpm_compiler.f90
index a499bb9..389ba94 100644
--- a/src/fpm_compiler.f90
+++ b/src/fpm_compiler.f90
@@ -35,7 +35,8 @@ use fpm_environment, only: &
OS_WINDOWS, &
OS_CYGWIN, &
OS_SOLARIS, &
- OS_FREEBSD
+ OS_FREEBSD, &
+ OS_OPENBSD
implicit none
public :: is_unknown_compiler
public :: get_module_flags
@@ -239,7 +240,6 @@ subroutine get_debug_compile_flags(id, flags)
& -g&
& -assume byterecl&
& -traceback&
- & -coarray=single&
&'
case(id_intel_classic_mac)
flags = '&
@@ -260,7 +260,6 @@ subroutine get_debug_compile_flags(id, flags)
& /Z7&
& /assume:byterecl&
& /traceback&
- & /Qcoarray:single&
&'
case(id_intel_llvm_nix, id_intel_llvm_unknown)
flags = '&
@@ -271,7 +270,6 @@ subroutine get_debug_compile_flags(id, flags)
& -g&
& -assume byterecl&
& -traceback&
- & -coarray=single&
&'
case(id_intel_llvm_windows)
flags = '&
@@ -281,7 +279,6 @@ subroutine get_debug_compile_flags(id, flags)
& /Od&
& /Z7&
& /assume:byterecl&
- & /Qcoarray:single&
&'
case(id_nag)
flags = '&
@@ -332,6 +329,34 @@ subroutine get_module_flags(compiler, modpath, flags)
end subroutine get_module_flags
+subroutine get_default_c_compiler(f_compiler, c_compiler)
+ character(len=*), intent(in) :: f_compiler
+ character(len=:), allocatable, intent(out) :: c_compiler
+ integer(compiler_enum) :: id
+
+ id = get_compiler_id(f_compiler)
+
+ select case(id)
+
+ case(id_intel_classic_nix, id_intel_classic_mac, id_intel_classic_windows, id_intel_classic_unknown)
+ c_compiler = 'icc'
+
+ case(id_intel_llvm_nix,id_intel_llvm_windows, id_intel_llvm_unknown)
+ c_compiler = 'icx'
+
+ case(id_flang)
+ c_compiler='clang'
+
+ case(id_ibmxl)
+ c_compiler='xlc'
+
+ case default
+ ! Fall-back to using Fortran compiler
+ c_compiler = f_compiler
+ end select
+
+end subroutine get_default_c_compiler
+
function get_compiler_id(compiler) result(id)
character(len=*), intent(in) :: compiler
integer(kind=compiler_enum) :: id
diff --git a/src/fpm_environment.f90 b/src/fpm_environment.f90
index 982380d..107c977 100644
--- a/src/fpm_environment.f90
+++ b/src/fpm_environment.f90
@@ -1,5 +1,5 @@
!> This module contains procedures that interact with the programming environment.
-!!
+!!
!! * [get_os_type] -- Determine the OS type
!! * [get_env] -- return the value of an environment variable
module fpm_environment
@@ -9,6 +9,7 @@ module fpm_environment
public :: os_is_unix
public :: run
public :: get_env
+ public :: get_archiver
integer, parameter, public :: OS_UNKNOWN = 0
integer, parameter, public :: OS_LINUX = 1
@@ -17,12 +18,13 @@ module fpm_environment
integer, parameter, public :: OS_CYGWIN = 4
integer, parameter, public :: OS_SOLARIS = 5
integer, parameter, public :: OS_FREEBSD = 6
+ integer, parameter, public :: OS_OPENBSD = 7
contains
!> Determine the OS type
integer function get_os_type() result(r)
!!
!! Returns one of OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, OS_CYGWIN,
- !! OS_SOLARIS, OS_FREEBSD.
+ !! OS_SOLARIS, OS_FREEBSD, OS_OPENBSD.
!!
!! At first, the environment variable `OS` is checked, which is usually
!! found on Windows. Then, `OSTYPE` is read in and compared with common
@@ -83,6 +85,12 @@ contains
r = OS_FREEBSD
return
end if
+
+ ! OpenBSD
+ if (index(val, 'OpenBSD') > 0 .or. index(val, 'openbsd') > 0) then
+ r = OS_OPENBSD
+ return
+ end if
end if
! Linux
@@ -110,7 +118,7 @@ contains
end if
end function get_os_type
- !> Compare the output of [[get_os_type]] or the optional
+ !> Compare the output of [[get_os_type]] or the optional
!! passed INTEGER value to the value for OS_WINDOWS
!! and return .TRUE. if they match and .FALSE. otherwise
logical function os_is_unix(os) result(unix)
@@ -157,7 +165,7 @@ contains
function get_env(NAME,DEFAULT) result(VALUE)
implicit none
!> name of environment variable to get the value of
- character(len=*),intent(in) :: NAME
+ character(len=*),intent(in) :: NAME
!> default value to return if the requested value is undefined or blank
character(len=*),intent(in),optional :: DEFAULT
!> the returned value
@@ -189,4 +197,24 @@ contains
if(VALUE.eq.''.and.present(DEFAULT))VALUE=DEFAULT
end function get_env
+ function get_archiver() result(archiver)
+ character(:), allocatable :: archiver
+
+ associate(os_type => get_os_type())
+ if (os_type /= OS_WINDOWS .and. os_type /= OS_UNKNOWN) then
+ archiver = "ar -rs "
+ else
+ block
+ integer :: estat
+
+ call execute_command_line("ar --version", exitstat=estat)
+ if (estat /= 0) then
+ archiver = "lib /OUT:"
+ else
+ archiver = "ar -rs "
+ end if
+ end block
+ end if
+ end associate
+ end function
end module fpm_environment
diff --git a/src/fpm_filesystem.f90 b/src/fpm_filesystem.f90
index 6acd383..c9c97dd 100644
--- a/src/fpm_filesystem.f90
+++ b/src/fpm_filesystem.f90
@@ -4,13 +4,13 @@ module fpm_filesystem
use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit
use fpm_environment, only: get_os_type, &
OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, &
- OS_CYGWIN, OS_SOLARIS, OS_FREEBSD
+ OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD
use fpm_strings, only: f_string, replace, string_t, split
implicit none
private
public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, read_lines, list_files, env_variable, &
mkdir, exists, get_temp_filename, windows_path, unix_path, getline, delete_file, to_fortran_name
- public :: fileopen, fileclose, filewrite, warnwrite
+ public :: fileopen, fileclose, filewrite, warnwrite, parent_dir
integer, parameter :: LINE_BUFFER_LEN = 1000
@@ -184,6 +184,15 @@ function dirname(path) result (dir)
end function dirname
+!> Extract dirname from path
+function parent_dir(path) result (dir)
+ character(*), intent(in) :: path
+ character(:), allocatable :: dir
+
+ dir = path(1:scan(path,'/\',back=.true.)-1)
+
+end function parent_dir
+
!> test if a name matches an existing directory path
logical function is_dir(dir)
@@ -192,7 +201,7 @@ logical function is_dir(dir)
select case (get_os_type())
- case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD)
+ case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD)
call execute_command_line("test -d " // dir , exitstat=stat)
case (OS_WINDOWS)
@@ -214,7 +223,7 @@ function join_path(a1,a2,a3,a4,a5) result(path)
character(len=1) :: filesep
select case (get_os_type())
- case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD)
+ case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD)
filesep = '/'
case (OS_WINDOWS)
filesep = '\'
@@ -283,7 +292,7 @@ subroutine mkdir(dir)
if (is_dir(dir)) return
select case (get_os_type())
- case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD)
+ case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD)
call execute_command_line('mkdir -p ' // dir, exitstat=stat)
write (*, '(" + ",2a)') 'mkdir -p ' // dir
@@ -322,7 +331,7 @@ recursive subroutine list_files(dir, files, recurse)
allocate (temp_file, source=get_temp_filename())
select case (get_os_type())
- case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD)
+ case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD)
call execute_command_line('ls -A ' // dir // ' > ' // temp_file, &
exitstat=stat)
case (OS_WINDOWS)
diff --git a/src/fpm_model.f90 b/src/fpm_model.f90
index bfb0115..9746e5f 100644
--- a/src/fpm_model.f90
+++ b/src/fpm_model.f90
@@ -1,6 +1,6 @@
!># The fpm package model
!>
-!> Defines the fpm model data types which encapsulate all information
+!> Defines the fpm model data types which encapsulate all information
!> required to correctly build a package and its dependencies.
!>
!> The process (see `[[build_model(subroutine)]]`) for generating a valid `[[fpm_model]]` involves
@@ -117,6 +117,12 @@ type :: fpm_model_t
!> Command line name to invoke fortran compiler
character(:), allocatable :: fortran_compiler
+ !> Command line to invoke for creating static library
+ character(:), allocatable :: archiver
+
+ !> Command line name to invoke c compiler
+ character(:), allocatable :: c_compiler
+
!> Command line flags passed to fortran for compilation
character(:), allocatable :: fortran_compile_flags
@@ -128,7 +134,10 @@ type :: fpm_model_t
!> Native libraries to link against
type(string_t), allocatable :: link_libraries(:)
-
+
+ !> External modules used
+ type(string_t), allocatable :: external_modules(:)
+
!> Project dependencies
type(dependency_tree_t) :: deps
@@ -276,6 +285,13 @@ function info_model(model) result(s)
if (i < size(model%link_libraries)) s = s // ", "
end do
s = s // "]"
+ ! type(string_t), allocatable :: external_modules(:)
+ s = s // ", external_modules=["
+ do i = 1, size(model%external_modules)
+ s = s // '"' // model%external_modules(i)%s // '"'
+ if (i < size(model%external_modules)) s = s // ", "
+ end do
+ s = s // "]"
! type(dependency_tree_t) :: deps
! TODO: print `dependency_tree_t` properly, which should become part of the
! model, not imported from another file
diff --git a/src/fpm_os.F90 b/src/fpm_os.F90
new file mode 100644
index 0000000..71663fe
--- /dev/null
+++ b/src/fpm_os.F90
@@ -0,0 +1,105 @@
+module fpm_os
+ use, intrinsic :: iso_c_binding, only : c_char, c_int, c_null_char, c_ptr, c_associated
+ use fpm_error, only : error_t, fatal_error
+ implicit none
+ private
+ public :: change_directory, get_current_directory
+
+#ifndef _WIN32
+ character(len=*), parameter :: pwd_env = "PWD"
+#else
+ character(len=*), parameter :: pwd_env = "CD"
+#endif
+
+ interface
+ function chdir(path) result(stat) &
+#ifndef _WIN32
+ bind(C, name="chdir")
+#else
+ bind(C, name="_chdir")
+#endif
+ import :: c_char, c_int
+ character(kind=c_char, len=1), intent(in) :: path(*)
+ integer(c_int) :: stat
+ end function chdir
+
+ function getcwd(buf, bufsize) result(path) &
+#ifndef _WIN32
+ bind(C, name="getcwd")
+#else
+ bind(C, name="_getcwd")
+#endif
+ import :: c_char, c_int, c_ptr
+ character(kind=c_char, len=1), intent(in) :: buf(*)
+ integer(c_int), value, intent(in) :: bufsize
+ type(c_ptr) :: path
+ end function getcwd
+ end interface
+
+contains
+
+ subroutine change_directory(path, error)
+ character(len=*), intent(in) :: path
+ type(error_t), allocatable, intent(out) :: error
+
+ character(kind=c_char, len=1), allocatable :: cpath(:)
+ integer :: stat
+
+ allocate(cpath(len(path)+1))
+ call f_c_character(path, cpath, len(path)+1)
+
+ stat = chdir(cpath)
+
+ if (stat /= 0) then
+ call fatal_error(error, "Failed to change directory to '"//path//"'")
+ end if
+ end subroutine change_directory
+
+ subroutine get_current_directory(path, error)
+ character(len=:), allocatable, intent(out) :: path
+ type(error_t), allocatable, intent(out) :: error
+
+ character(kind=c_char, len=1), allocatable :: cpath(:)
+ integer(c_int), parameter :: buffersize = 1000_c_int
+ type(c_ptr) :: tmp
+
+ allocate(cpath(buffersize))
+
+ tmp = getcwd(cpath, buffersize)
+ if (c_associated(tmp)) then
+ call c_f_character(cpath, path)
+ else
+ call fatal_error(error, "Failed to retrieve current directory")
+ end if
+
+ end subroutine get_current_directory
+
+ subroutine f_c_character(rhs, lhs, len)
+ character(kind=c_char), intent(out) :: lhs(*)
+ character(len=*), intent(in) :: rhs
+ integer, intent(in) :: len
+ integer :: length
+ length = min(len-1, len_trim(rhs))
+
+ lhs(1:length) = transfer(rhs(1:length), lhs(1:length))
+ lhs(length+1:length+1) = c_null_char
+
+ end subroutine f_c_character
+
+ subroutine c_f_character(rhs, lhs)
+ character(kind=c_char), intent(in) :: rhs(*)
+ character(len=:), allocatable, intent(out) :: lhs
+
+ integer :: ii
+
+ do ii = 1, huge(ii) - 1
+ if (rhs(ii) == c_null_char) then
+ exit
+ end if
+ end do
+ allocate(character(len=ii-1) :: lhs)
+ lhs = transfer(rhs(1:ii-1), lhs)
+
+ end subroutine c_f_character
+
+end module fpm_os
diff --git a/src/fpm_targets.f90 b/src/fpm_targets.f90
index 02bb600..c247232 100644
--- a/src/fpm_targets.f90
+++ b/src/fpm_targets.f90
@@ -35,7 +35,8 @@ implicit none
private
public FPM_TARGET_UNKNOWN, FPM_TARGET_EXECUTABLE, &
- FPM_TARGET_ARCHIVE, FPM_TARGET_OBJECT
+ FPM_TARGET_ARCHIVE, FPM_TARGET_OBJECT, &
+ FPM_TARGET_C_OBJECT
public build_target_t, build_target_ptr
public targets_from_sources, resolve_module_dependencies
public resolve_target_linking, add_target, add_dependency
@@ -50,7 +51,8 @@ integer, parameter :: FPM_TARGET_EXECUTABLE = 1
integer, parameter :: FPM_TARGET_ARCHIVE = 2
!> Target type is compiled object
integer, parameter :: FPM_TARGET_OBJECT = 3
-
+!> Target type is c compiled object
+integer, parameter :: FPM_TARGET_C_OBJECT = 4
!> Wrapper type for constructing arrays of `[[build_target_t]]` pointers
type build_target_ptr
@@ -121,7 +123,7 @@ subroutine targets_from_sources(targets,model,error)
call build_target_list(targets,model)
- call resolve_module_dependencies(targets,error)
+ call resolve_module_dependencies(targets,model%external_modules,error)
if (allocated(error)) return
call resolve_target_linking(targets,model)
@@ -194,7 +196,8 @@ subroutine build_target_list(targets,model)
case (FPM_UNIT_MODULE,FPM_UNIT_SUBMODULE,FPM_UNIT_SUBPROGRAM,FPM_UNIT_CSOURCE)
call add_target(targets,source = sources(i), &
- type = FPM_TARGET_OBJECT,&
+ type = merge(FPM_TARGET_C_OBJECT,FPM_TARGET_OBJECT,&
+ sources(i)%unit_type==FPM_UNIT_CSOURCE), &
output_file = get_object_name(sources(i)))
if (with_lib .and. sources(i)%unit_scope == FPM_SCOPE_LIB) then
@@ -345,8 +348,9 @@ end subroutine add_dependency
!> a source file in the package of the correct scope, then a __fatal error__
!> is returned by the procedure and model construction fails.
!>
-subroutine resolve_module_dependencies(targets,error)
+subroutine resolve_module_dependencies(targets,external_modules,error)
type(build_target_ptr), intent(inout), target :: targets(:)
+ type(string_t), intent(in) :: external_modules(:)
type(error_t), allocatable, intent(out) :: error
type(build_target_ptr) :: dep
@@ -364,6 +368,11 @@ subroutine resolve_module_dependencies(targets,error)
cycle
end if
+ if (targets(i)%ptr%source%modules_used(j)%s .in. external_modules) then
+ ! Dependency satisfied in system-installed module
+ cycle
+ end if
+
if (any(targets(i)%ptr%source%unit_scope == &
[FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST])) then
dep%ptr => &
@@ -442,7 +451,7 @@ subroutine resolve_target_linking(targets, model)
integer :: i
character(:), allocatable :: global_link_flags
- character(:), allocatable :: global_compile_flags
+ character(:), allocatable :: global_include_flags
if (size(targets) == 0) return
@@ -452,17 +461,16 @@ subroutine resolve_target_linking(targets, model)
allocate(character(0) :: global_link_flags)
end if
- global_compile_flags = model%fortran_compile_flags
-
if (allocated(model%link_libraries)) then
if (size(model%link_libraries) > 0) then
global_link_flags = global_link_flags // " -l" // string_cat(model%link_libraries," -l")
end if
end if
+ allocate(character(0) :: global_include_flags)
if (allocated(model%include_dirs)) then
if (size(model%include_dirs) > 0) then
- global_compile_flags = global_compile_flags // &
+ global_include_flags = global_include_flags // &
& " -I" // string_cat(model%include_dirs," -I")
end if
end if
@@ -471,7 +479,11 @@ subroutine resolve_target_linking(targets, model)
associate(target => targets(i)%ptr)
- target%compile_flags = global_compile_flags
+ if (target%target_type /= FPM_TARGET_C_OBJECT) then
+ target%compile_flags = model%fortran_compile_flags//" "//global_include_flags
+ else
+ target%compile_flags = global_include_flags
+ end if
allocate(target%link_objects(0))