diff options
author | Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> | 2020-12-09 09:42:25 +0100 |
---|---|---|
committer | Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> | 2020-12-10 18:14:23 +0100 |
commit | 4eeab74dd007d9476f3b862f79c623e457a7dad1 (patch) | |
tree | 0d2f6438044a8ec264ae97a4ca82430a905866aa | |
parent | 50723700d253c83e4b68930370c3b0819e529be3 (diff) | |
download | fpm-4eeab74dd007d9476f3b862f79c623e457a7dad1.tar.gz fpm-4eeab74dd007d9476f3b862f79c623e457a7dad1.zip |
Implement example applications in Fortran fpm
-rwxr-xr-x | ci/run_tests.bat | 13 | ||||
-rwxr-xr-x | ci/run_tests.sh | 6 | ||||
-rw-r--r-- | example_packages/README.md | 1 | ||||
-rw-r--r-- | example_packages/with_examples/.gitignore | 1 | ||||
-rw-r--r-- | example_packages/with_examples/demo/prog.f90 | 3 | ||||
-rw-r--r-- | example_packages/with_examples/fpm.toml | 7 | ||||
-rw-r--r-- | fpm/src/fpm.f90 | 36 | ||||
-rw-r--r-- | fpm/src/fpm/manifest.f90 | 31 | ||||
-rw-r--r-- | fpm/src/fpm/manifest/build.f90 | 15 | ||||
-rw-r--r-- | fpm/src/fpm/manifest/example.f90 | 175 | ||||
-rw-r--r-- | fpm/src/fpm/manifest/package.f90 | 32 | ||||
-rw-r--r-- | fpm/src/fpm_command_line.f90 | 20 | ||||
-rw-r--r-- | fpm/src/fpm_model.f90 | 3 | ||||
-rw-r--r-- | fpm/src/fpm_targets.f90 | 8 | ||||
-rw-r--r-- | fpm/test/fpm_test/test_manifest.f90 | 54 |
15 files changed, 381 insertions, 24 deletions
diff --git a/ci/run_tests.bat b/ci/run_tests.bat index 22be2db..42f391c 100755 --- a/ci/run_tests.bat +++ b/ci/run_tests.bat @@ -109,6 +109,17 @@ if errorlevel 1 exit 1 .\build\gfortran_debug\test\farewell_test +cd ..\with_examples +if errorlevel 1 exit 1 + +del /q /f build +%fpm_path% build +if errorlevel 1 exit 1 + +.\build\gfortran_debug\app\demo-prog +if errorlevel 1 exit 1 + + cd ..\auto_discovery_off if errorlevel 1 exit 1 @@ -167,4 +178,4 @@ if errorlevel 1 exit 1 .\build\gfortran_debug\app\gomp_test if errorlevel 1 exit 1 -cd ..\..
\ No newline at end of file +cd ..\.. diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 85484e5..7412fba 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -50,6 +50,10 @@ cd ../hello_complex_2 ./build/gfortran_debug/test/greet_test ./build/gfortran_debug/test/farewell_test +cd ../with_examples +"${f_fpm_path}" build +./build/gfortran_debug/app/demo-prog + cd ../auto_discovery_off "${f_fpm_path}" build ./build/gfortran_debug/app/auto_discovery_off @@ -77,4 +81,4 @@ cd ../link_executable ./build/gfortran_debug/app/gomp_test # Cleanup -rm -rf ./*/build
\ No newline at end of file +rm -rf ./*/build diff --git a/example_packages/README.md b/example_packages/README.md index 0eb0653..a360225 100644 --- a/example_packages/README.md +++ b/example_packages/README.md @@ -13,6 +13,7 @@ the features demonstrated in each package and which versions of fpm are supporte | hello_complex_2 | Auto-discovery of tests and executables with modules | N | Y | | hello_fpm | App-only; local path dependency | Y | Y | | hello_world | App-only | Y | Y | +| with_examples | Example-only | N | Y | | makefile_complex | External build command (makefile); local path dependency | Y | N | | program_with_module | App-only; module+program in single source file | Y | Y | | submodules | Lib-only; submodules (3 levels) | N | Y | diff --git a/example_packages/with_examples/.gitignore b/example_packages/with_examples/.gitignore new file mode 100644 index 0000000..d9b4f01 --- /dev/null +++ b/example_packages/with_examples/.gitignore @@ -0,0 +1 @@ +/build/* diff --git a/example_packages/with_examples/demo/prog.f90 b/example_packages/with_examples/demo/prog.f90 new file mode 100644 index 0000000..8b3d882 --- /dev/null +++ b/example_packages/with_examples/demo/prog.f90 @@ -0,0 +1,3 @@ +program demo + write(*, '(a)') "This is a simple demo program, but not a real application" +end program demo diff --git a/example_packages/with_examples/fpm.toml b/example_packages/with_examples/fpm.toml new file mode 100644 index 0000000..d7d2926 --- /dev/null +++ b/example_packages/with_examples/fpm.toml @@ -0,0 +1,7 @@ +name = "with_examples" +build.auto-examples = false + +[[example]] +name = "demo-prog" +source-dir = "demo" +main = "prog.f90" diff --git a/fpm/src/fpm.f90 b/fpm/src/fpm.f90 index f23e119..943d667 100644 --- a/fpm/src/fpm.f90 +++ b/fpm/src/fpm.f90 @@ -7,8 +7,8 @@ use fpm_dependency, only : new_dependency_tree use fpm_environment, only: run use fpm_filesystem, only: is_dir, join_path, number_of_rows, list_files, exists, basename use fpm_model, only: fpm_model_t, srcfile_t, build_target_t, & - FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, & - FPM_SCOPE_DEP, FPM_SCOPE_APP, FPM_SCOPE_TEST, & + FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, FPM_SCOPE_DEP, & + FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST, & FPM_TARGET_EXECUTABLE, FPM_TARGET_ARCHIVE use fpm_compiler, only: add_compile_flag_defaults @@ -86,6 +86,15 @@ subroutine build_model(model, settings, package, error) end if end if + if (is_dir('example') .and. package%build%auto_examples) then + call add_sources_from_dir(model%sources,'example', FPM_SCOPE_EXAMPLE, & + with_executables=.true., error=error) + + if (allocated(error)) then + return + end if + + end if if (is_dir('test') .and. package%build%auto_tests) then call add_sources_from_dir(model%sources,'test', FPM_SCOPE_TEST, & with_executables=.true., error=error) @@ -105,6 +114,16 @@ subroutine build_model(model, settings, package, error) end if end if + if (allocated(package%example)) then + call add_executable_sources(model%sources, package%example, FPM_SCOPE_EXAMPLE, & + auto_discover=package%build%auto_executables, & + error=error) + + if (allocated(error)) then + return + end if + + end if if (allocated(package%test)) then call add_executable_sources(model%sources, package%test, FPM_SCOPE_TEST, & auto_discover=package%build%auto_tests, & @@ -205,6 +224,7 @@ subroutine cmd_run(settings,test) type(string_t), allocatable :: executables(:) type(build_target_t), pointer :: exe_target type(srcfile_t), pointer :: exe_source + integer :: run_scope call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) if (allocated(error)) then @@ -218,6 +238,12 @@ subroutine cmd_run(settings,test) error stop 1 end if + if (test) then + run_scope = FPM_SCOPE_TEST + else + run_scope = merge(FPM_SCOPE_EXAMPLE, FPM_SCOPE_APP, settings%example) + end if + ! Enumerate executable targets to run col_width = -1 found(:) = .false. @@ -231,8 +257,7 @@ subroutine cmd_run(settings,test) exe_source => exe_target%dependencies(1)%ptr%source - if (exe_source%unit_scope == & - merge(FPM_SCOPE_TEST,FPM_SCOPE_APP,test)) then + if (exe_source%unit_scope == run_scope) then col_width = max(col_width,len(basename(exe_target%output_file))+2) @@ -295,8 +320,7 @@ subroutine cmd_run(settings,test) exe_source => exe_target%dependencies(1)%ptr%source - if (exe_source%unit_scope == & - merge(FPM_SCOPE_TEST,FPM_SCOPE_APP,test)) then + if (exe_source%unit_scope == run_scope) then write(stderr,'(A)',advance=(merge("yes","no ",modulo(j,nCol)==0))) & & [character(len=col_width) :: basename(exe_target%output_file)] diff --git a/fpm/src/fpm/manifest.f90 b/fpm/src/fpm/manifest.f90 index 2398d79..7e80b29 100644 --- a/fpm/src/fpm/manifest.f90 +++ b/fpm/src/fpm/manifest.f90 @@ -8,6 +8,7 @@ !> to hide the actual implementation details. module fpm_manifest use fpm_manifest_build, only: build_config_t + use fpm_manifest_example, only : example_config_t use fpm_manifest_executable, only : executable_config_t use fpm_manifest_dependency, only : dependency_config_t use fpm_manifest_library, only : library_config_t @@ -20,6 +21,7 @@ module fpm_manifest private public :: get_package_data, default_executable, default_library, default_test + public :: default_example public :: package_config_t, dependency_config_t @@ -52,6 +54,21 @@ contains end subroutine default_executable + !> Populate test in case we find the default example/ directory + subroutine default_example(self, name) + + !> Instance of the executable meta data + type(example_config_t), intent(out) :: self + + !> Name of the package + character(len=*), intent(in) :: name + + self%name = name // "-demo" + self%source_dir = "example" + self%main = "main.f90" + + end subroutine default_example + !> Populate test in case we find the default test/ directory subroutine default_test(self, name) @@ -61,7 +78,7 @@ contains !> Name of the package character(len=*), intent(in) :: name - self%name = name + self%name = name // "-test" self%source_dir = "test" self%main = "main.f90" @@ -135,6 +152,13 @@ contains call default_executable(package%executable(1), package%name) end if + ! Populate example in case we find the default example directory + if (.not.allocated(package%example) .and. & + exists(join_path("example","main.f90"))) then + allocate(package%example(1)) + call default_example(package%example(1), package%name) + endif + ! Populate test in case we find the default test directory if (.not.allocated(package%test) .and. & & exists(join_path(root, "test", "main.f90"))) then @@ -142,7 +166,10 @@ contains call default_test(package%test(1), package%name) endif - if (.not.(allocated(package%library) .or. allocated(package%executable))) then + if (.not.(allocated(package%library) & + & .or. allocated(package%executable) & + & .or. allocated(package%example) & + & .or. allocated(package%test))) then call fatal_error(error, "Neither library nor executable found, there is nothing to do") return end if diff --git a/fpm/src/fpm/manifest/build.f90 b/fpm/src/fpm/manifest/build.f90 index 85fd2c7..d96974f 100644 --- a/fpm/src/fpm/manifest/build.f90 +++ b/fpm/src/fpm/manifest/build.f90 @@ -5,6 +5,7 @@ !>```toml !>[build] !>auto-executables = bool +!>auto-examples = bool !>auto-tests = bool !>link = ["lib"] !>``` @@ -24,6 +25,9 @@ module fpm_manifest_build !> Automatic discovery of executables logical :: auto_executables + !> Automatic discovery of examples + logical :: auto_examples + !> Automatic discovery of tests logical :: auto_tests @@ -72,6 +76,14 @@ contains return end if + call get_value(table, "auto-examples", self%auto_examples, .true., stat=stat) + + if (stat /= toml_stat%success) then + call fatal_error(error,"Error while reading value for 'auto-examples' in fpm.toml, expecting logical") + return + end if + + call get_value(table, "link", self%link, error) if (allocated(error)) return @@ -98,7 +110,7 @@ contains do ikey = 1, size(list) select case(list(ikey)%key) - case("auto-executables", "auto-tests", "link") + case("auto-executables", "auto-examples", "auto-tests", "link") continue case default @@ -136,6 +148,7 @@ contains write(unit, fmt) "Build configuration" write(unit, fmt) " - auto-discovery (apps) ", merge("enabled ", "disabled", self%auto_executables) + write(unit, fmt) " - auto-discovery (examples) ", merge("enabled ", "disabled", self%auto_examples) write(unit, fmt) " - auto-discovery (tests) ", merge("enabled ", "disabled", self%auto_tests) if (allocated(self%link)) then write(unit, fmt) " - link against" diff --git a/fpm/src/fpm/manifest/example.f90 b/fpm/src/fpm/manifest/example.f90 new file mode 100644 index 0000000..fc2a0af --- /dev/null +++ b/fpm/src/fpm/manifest/example.f90 @@ -0,0 +1,175 @@ +!> Implementation of the meta data for an example. +!> +!> The example data structure is effectively a decorated version of an executable +!> and shares most of its properties, except for the defaults and can be +!> handled under most circumstances just like any other executable. +!> +!> A example table can currently have the following fields +!> +!>```toml +!>[[ example ]] +!>name = "string" +!>source-dir = "path" +!>main = "file" +!>link = ["lib"] +!>[example.dependencies] +!>``` +module fpm_manifest_example + use fpm_manifest_dependency, only : dependency_config_t, new_dependencies + use fpm_manifest_executable, only : executable_config_t + use fpm_error, only : error_t, syntax_error + use fpm_toml, only : toml_table, toml_key, toml_stat, get_value + implicit none + private + + public :: example_config_t, new_example + + + !> Configuation meta data for an example + type, extends(executable_config_t) :: example_config_t + + contains + + !> Print information on this instance + procedure :: info + + end type example_config_t + + +contains + + + !> Construct a new example configuration from a TOML data structure + subroutine new_example(self, table, error) + + !> Instance of the example configuration + type(example_config_t), intent(out) :: self + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table), pointer :: child + + call check(table, error) + if (allocated(error)) return + + call get_value(table, "name", self%name) + if (.not.allocated(self%name)) then + call syntax_error(error, "Could not retrieve example name") + return + end if + call get_value(table, "source-dir", self%source_dir, "example") + call get_value(table, "main", self%main, "main.f90") + + call get_value(table, "dependencies", child, requested=.false.) + if (associated(child)) then + call new_dependencies(self%dependency, child, error) + if (allocated(error)) return + end if + + call get_value(table, "link", self%link, error) + if (allocated(error)) return + + end subroutine new_example + + + !> Check local schema for allowed entries + subroutine check(table, error) + + !> Instance of the TOML data structure + type(toml_table), intent(inout) :: table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_key), allocatable :: list(:) + logical :: name_present + integer :: ikey + + name_present = .false. + + call table%get_keys(list) + + if (size(list) < 1) then + call syntax_error(error, "Example section does not provide sufficient entries") + return + end if + + do ikey = 1, size(list) + select case(list(ikey)%key) + case default + call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in example entry") + exit + + case("name") + name_present = .true. + + case("source-dir", "main", "dependencies", "link") + continue + + end select + end do + if (allocated(error)) return + + if (.not.name_present) then + call syntax_error(error, "Example name is not provided, please add a name entry") + end if + + end subroutine check + + + !> Write information on instance + subroutine info(self, unit, verbosity) + + !> Instance of the example configuration + class(example_config_t), intent(in) :: self + + !> Unit for IO + integer, intent(in) :: unit + + !> Verbosity of the printout + integer, intent(in), optional :: verbosity + + integer :: pr, ii + character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)', & + & fmti = '("#", 1x, a, t30, i0)' + + if (present(verbosity)) then + pr = verbosity + else + pr = 1 + end if + + if (pr < 1) return + + write(unit, fmt) "Example target" + if (allocated(self%name)) then + write(unit, fmt) "- name", self%name + end if + if (allocated(self%source_dir)) then + if (self%source_dir /= "example" .or. pr > 2) then + write(unit, fmt) "- source directory", self%source_dir + end if + end if + if (allocated(self%main)) then + if (self%main /= "main.f90" .or. pr > 2) then + write(unit, fmt) "- example source", self%main + end if + end if + + if (allocated(self%dependency)) then + if (size(self%dependency) > 1 .or. pr > 2) then + write(unit, fmti) "- dependencies", size(self%dependency) + end if + do ii = 1, size(self%dependency) + call self%dependency(ii)%info(unit, pr - 1) + end do + end if + + end subroutine info + + +end module fpm_manifest_example diff --git a/fpm/src/fpm/manifest/package.f90 b/fpm/src/fpm/manifest/package.f90 index 987f2d1..2faf328 100644 --- a/fpm/src/fpm/manifest/package.f90 +++ b/fpm/src/fpm/manifest/package.f90 @@ -25,11 +25,13 @@ !>[dependencies] !>[dev-dependencies] !>[[ executable ]] +!>[[ example ]] !>[[ test ]] !>``` module fpm_manifest_package use fpm_manifest_build, only: build_config_t, new_build_config use fpm_manifest_dependency, only : dependency_config_t, new_dependencies + use fpm_manifest_example, only : example_config_t, new_example use fpm_manifest_executable, only : executable_config_t, new_executable use fpm_manifest_library, only : library_config_t, new_library use fpm_manifest_test, only : test_config_t, new_test @@ -67,6 +69,9 @@ module fpm_manifest_package !> Development dependency meta data type(dependency_config_t), allocatable :: dev_dependency(:) + !> Example meta data + type(example_config_t), allocatable :: example(:) + !> Test meta data type(test_config_t), allocatable :: test(:) @@ -174,6 +179,22 @@ contains if (allocated(error)) return end if + call get_value(table, "example", children, requested=.false.) + if (associated(children)) then + nn = len(children) + allocate(self%example(nn)) + do ii = 1, nn + call get_value(children, ii, node, stat=stat) + if (stat /= toml_stat%success) then + call fatal_error(error, "Could not retrieve example from array entry") + exit + end if + call new_example(self%example(ii), node, error) + if (allocated(error)) exit + end do + if (allocated(error)) return + end if + call get_value(table, "test", children, requested=.false.) if (associated(children)) then nn = len(children) @@ -230,7 +251,7 @@ contains case("version", "license", "author", "maintainer", "copyright", & & "description", "keywords", "categories", "homepage", "build", & & "dependencies", "dev-dependencies", "test", "executable", & - & "library") + & "example", "library") continue end select @@ -298,6 +319,15 @@ contains end do end if + if (allocated(self%example)) then + if (size(self%example) > 1 .or. pr > 2) then + write(unit, fmti) "- examples", size(self%example) + end if + do ii = 1, size(self%example) + call self%example(ii)%info(unit, pr - 1) + end do + end if + if (allocated(self%test)) then if (size(self%test) > 1 .or. pr > 2) then write(unit, fmti) "- tests", size(self%test) diff --git a/fpm/src/fpm_command_line.f90 b/fpm/src/fpm_command_line.f90 index ca15916..96a335f 100644 --- a/fpm/src/fpm_command_line.f90 +++ b/fpm/src/fpm_command_line.f90 @@ -67,6 +67,7 @@ type, extends(fpm_build_settings) :: fpm_run_settings character(len=ibug),allocatable :: name(:) character(len=:),allocatable :: args character(len=:),allocatable :: runner + logical :: example end type type, extends(fpm_run_settings) :: fpm_test_settings @@ -142,6 +143,7 @@ contains & --target " " & & --list F & & --release F& + & --example F& & --runner " " & & --compiler "'//get_env('FPM_COMPILER','gfortran')//'" & & --verbose F& @@ -166,6 +168,7 @@ contains & args=remaining,& & build_name=val_build,& & compiler=val_compiler, & + & example=lget('example'), & & list=lget('list'),& & name=names,& & runner=val_runner,& @@ -334,6 +337,7 @@ contains & args=remaining, & & build_name=val_build, & & compiler=val_compiler, & + & example=.false., & & list=lget('list'), & & name=names, & & runner=val_runner, & @@ -359,7 +363,7 @@ contains call set_args('& & --list F& & --verbose F& - ', help_fpm, version_text) + &', help_fpm, version_text) ! Note: will not get here if --version or --usage or --help ! is present on commandline help_text=help_usage @@ -451,7 +455,7 @@ contains ' new NAME [--lib|--src] [--app] [--test] [--backfill] ', & ' update [NAME(s)] [--fetch-only] [--clean] [--verbose] ', & ' list [--list] ', & - ' run [[--target] NAME(s)] [--release] [--runner "CMD"] [--list] ', & + ' run [[--target] NAME(s)] [--release] [--runner "CMD"] [--list] [--example] ', & ' [--compiler COMPILER_NAME] [-- ARGS] ', & ' test [[--target] NAME(s)] [--release] [--runner "CMD"] [--list] ', & ' [--compiler COMPILER_NAME] [-- ARGS] ', & @@ -629,17 +633,21 @@ contains ' ', & 'SYNOPSIS ', & ' fpm run [[--target] NAME(s)][--release][--compiler COMPILER_NAME] ', & - ' [--runner "CMD"] [--list][-- ARGS] ', & + ' [--runner "CMD"] [--example] [--list][-- ARGS] ', & ' ', & ' fpm run --help|--version ', & ' ', & 'DESCRIPTION ', & ' Run applications you have built in your fpm(1) project. ', & + ' By default applications specified in as "executable" in your package ', & + ' manifest are used, alternatively also demonstration programs under ', & + ' "example" can be used with this subcommand. ', & ' ', & 'OPTIONS ', & ' --target NAME(s) optional list of specific names to execute. ', & ' The default is to run all the applications in app/ ', & ' or the programs listed in the "fpm.toml" file. ', & + ' --example run example programs instead of applications ', & ' --release selects the optimized build instead of the debug ', & ' build. ', & ' --compiler COMPILER_NAME Specify a compiler name. The default is ', & @@ -657,11 +665,15 @@ contains ' ', & ' # run default programs in /app or as specified in "fpm.toml" ', & ' fpm run ', & - + ' ', & ' # run default programs in /app or as specified in "fpm.toml" ', & ' # using the compiler command "f90". ', & ' fpm run --compiler f90 ', & ' ', & + ' # run example and demonstration programs instead of the default ', & + ' # application programs (specified in "fpm.toml") ', & + ' fpm run --example ', & + ' ', & ' # run a specific program and pass arguments to the command ', & ' fpm run mytest -- -x 10 -y 20 --title "my title line" ', & ' ', & diff --git a/fpm/src/fpm_model.f90 b/fpm/src/fpm_model.f90 index a40aef0..b7d97db 100644 --- a/fpm/src/fpm_model.f90 +++ b/fpm/src/fpm_model.f90 @@ -36,7 +36,7 @@ public :: fpm_model_t, srcfile_t, build_target_t, build_target_ptr public :: FPM_UNIT_UNKNOWN, FPM_UNIT_PROGRAM, FPM_UNIT_MODULE, & FPM_UNIT_SUBMODULE, FPM_UNIT_SUBPROGRAM, FPM_UNIT_CSOURCE, & FPM_UNIT_CHEADER, FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, & - FPM_SCOPE_DEP, FPM_SCOPE_APP, FPM_SCOPE_TEST, & + FPM_SCOPE_DEP, FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST, & FPM_TARGET_UNKNOWN, FPM_TARGET_EXECUTABLE, FPM_TARGET_ARCHIVE, & FPM_TARGET_OBJECT @@ -66,6 +66,7 @@ integer, parameter :: FPM_SCOPE_DEP = 2 integer, parameter :: FPM_SCOPE_APP = 3 !> Module-use scope is library/dependency and test modules integer, parameter :: FPM_SCOPE_TEST = 4 +integer, parameter :: FPM_SCOPE_EXAMPLE = 5 !> Target type is unknown (ignored) diff --git a/fpm/src/fpm_targets.f90 b/fpm/src/fpm_targets.f90 index fd810f4..fb5a8ac 100644 --- a/fpm/src/fpm_targets.f90 +++ b/fpm/src/fpm_targets.f90 @@ -99,7 +99,7 @@ subroutine targets_from_sources(model,sources) source = sources(i) & ) - if (sources(i)%unit_scope == FPM_SCOPE_APP) then + if (any(sources(i)%unit_scope == [FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE])) then call add_target(model%targets,type = FPM_TARGET_EXECUTABLE,& link_libraries = sources(i)%link_libraries, & output_file = join_path(model%output_directory,'app', & @@ -151,7 +151,7 @@ subroutine targets_from_sources(model,sources) select case(source%unit_scope) - case (FPM_SCOPE_APP) + case (FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE) object_file = join_path(model%output_directory,'app',object_file)//'.o' case (FPM_SCOPE_TEST) @@ -258,8 +258,8 @@ subroutine resolve_module_dependencies(targets,error) cycle end if - if (targets(i)%ptr%source%unit_scope == FPM_SCOPE_APP .OR. & - targets(i)%ptr%source%unit_scope == FPM_SCOPE_TEST ) then + if (any(targets(i)%ptr%source%unit_scope == & + [FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST])) then dep%ptr => & find_module_dependency(targets,targets(i)%ptr%source%modules_used(j)%s, & include_dir = dirname(targets(i)%ptr%source%file_name)) diff --git a/fpm/test/fpm_test/test_manifest.f90 b/fpm/test/fpm_test/test_manifest.f90 index 1c2cfb7..d8adf50 100644 --- a/fpm/test/fpm_test/test_manifest.f90 +++ b/fpm/test/fpm_test/test_manifest.f90 @@ -53,9 +53,11 @@ contains & new_unittest("test-typeerror", test_test_typeerror, should_fail=.true.), & & new_unittest("test-noname", test_test_noname, should_fail=.true.), & & new_unittest("test-wrongkey", test_test_wrongkey, should_fail=.true.), & - & new_unittest("test-link-string", test_link_string), & - & new_unittest("test-link-array", test_link_array), & - & new_unittest("test-link-error", test_invalid_link, should_fail=.true.)] + & new_unittest("link-string", test_link_string), & + & new_unittest("link-array", test_link_array), & + & new_unittest("link-error", test_invalid_link, should_fail=.true.), & + & new_unittest("example-simple", test_example_simple), & + & new_unittest("example-empty", test_example_empty, should_fail=.true.)] end subroutine collect_manifest @@ -881,6 +883,52 @@ contains end subroutine test_test_wrongkey + !> Create a simple example entry + subroutine test_example_simple(error) + use fpm_manifest_example + use fpm_toml, only : new_table, set_value, add_table, toml_table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(toml_table), pointer :: child + integer :: stat + type(example_config_t) :: example + + call new_table(table) + call set_value(table, 'name', '"example"', stat) + call set_value(table, 'source-dir', '"demos"', stat) + call set_value(table, 'main', '"demo.f90"', stat) + call add_table(table, 'dependencies', child, stat) + + call new_example(example, table, error) + if (allocated(error)) return + + call check_string(error, example%main, "demo.f90", "Example main") + if (allocated(error)) return + + end subroutine test_example_simple + + + !> Examples cannot be created from empty tables + subroutine test_example_empty(error) + use fpm_manifest_example + use fpm_toml, only : new_table, toml_table + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + type(toml_table) :: table + type(example_config_t) :: example + + call new_table(table) + + call new_example(example, table, error) + + end subroutine test_example_empty + + !> Test link options subroutine test_link_string(error) use fpm_manifest_build |