diff options
-rw-r--r-- | .github/workflows/CI.yml | 12 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | ci/run_tests.bat | 6 | ||||
-rwxr-xr-x | ci/run_tests.sh | 2 | ||||
-rw-r--r-- | docs.md | 47 | ||||
-rw-r--r-- | example_packages/hello_complex_2/app/say_hello/app_extra_mod.f90 | 6 | ||||
-rw-r--r-- | example_packages/hello_complex_2/app/say_hello/app_hello_mod.f90 | 1 | ||||
-rw-r--r-- | example_packages/hello_complex_2/app/say_hello/say_Hello.f90 | 4 | ||||
-rw-r--r-- | fpm/fpm.toml | 2 | ||||
-rw-r--r-- | fpm/src/fpm.f90 | 1 | ||||
-rw-r--r-- | fpm/src/fpm/toml.f90 | 5 | ||||
-rw-r--r-- | fpm/src/fpm_command_line.f90 | 26 | ||||
-rw-r--r-- | fpm/src/fpm_targets.f90 | 93 | ||||
-rw-r--r-- | fpm/test/fpm_test/test_module_dependencies.f90 | 83 |
14 files changed, 224 insertions, 66 deletions
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f42d8ff..9ab8f5c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -30,8 +30,8 @@ jobs: TEST_SCRIPT: ci/run_tests.sh GET_VERSION_CMD: echo ${{ github.ref }} | cut -dv -f2 CHECK_VERSION_CMD: grep $(cat fpm_version) - RELEASE_CMD: "cp -- fpm-v$(cat fpm_version)-linux-x86_64" - BOOTSTRAP_RELEASE_CMD: cp /home/runner/.local/bin/fpm fpm-bootstrap-v$(cat fpm_version)-linux-x86_64 + RELEASE_CMD: "cp -- fpm-$(cat fpm_version)-linux-x86_64" + BOOTSTRAP_RELEASE_CMD: cp /home/runner/.local/bin/fpm fpm-haskell-$(cat fpm_version)-linux-x86_64 HASH_CMD: ls fpm-*|xargs -i{} sh -c 'sha256sum $1 > $1.sha256' -- {} RELEASE_FLAGS: --flag --static --flag -g --flag -fbacktrace --flag -O3 @@ -43,8 +43,8 @@ jobs: TEST_SCRIPT: ci/run_tests.sh GET_VERSION_CMD: echo ${{ github.ref }} | cut -dv -f2 CHECK_VERSION_CMD: grep $(cat fpm_version) - RELEASE_CMD: "cp -- fpm-v$(cat fpm_version)-macos-x86_64" - BOOTSTRAP_RELEASE_CMD: cp /Users/runner/.local/bin/fpm fpm-bootstrap-v$(cat fpm_version)-macos-x86_64 + RELEASE_CMD: "cp -- fpm-$(cat fpm_version)-macos-x86_64" + BOOTSTRAP_RELEASE_CMD: cp /Users/runner/.local/bin/fpm fpm-haskell-$(cat fpm_version)-macos-x86_64 HASH_CMD: ls fpm-*|xargs -I{} sh -c 'shasum -a 256 $1 > $1.sha256' -- {} RELEASE_FLAGS: --flag -g --flag -fbacktrace --flag -O3 @@ -56,8 +56,8 @@ jobs: TEST_SCRIPT: ci\run_tests.bat GET_VERSION_CMD: ("${{ github.ref }}" -Split "v")[1] CHECK_VERSION_CMD: Select-String -Pattern Version | Where-Object { if ($_ -like -join("*",(Get-Content fpm_version),"*")) {echo $_} else {Throw} } - RELEASE_CMD: copy -- (-join("fpm-v",(Get-Content fpm_version),"-windows-x86_64.exe")) - BOOTSTRAP_RELEASE_CMD: copy C:\Users\runneradmin\AppData\Roaming\local\bin\fpm.exe (-join("fpm-bootstrap-v",(Get-Content fpm_version),"-windows-x86_64.exe")) + RELEASE_CMD: copy -- (-join("fpm-",(Get-Content fpm_version),"-windows-x86_64.exe")) + BOOTSTRAP_RELEASE_CMD: copy C:\Users\runneradmin\AppData\Roaming\local\bin\fpm.exe (-join("fpm-haskell-",(Get-Content fpm_version),"-windows-x86_64.exe")) HASH_CMD: Get-ChildItem -File -Filter "fpm-*" | Foreach-Object {echo (Get-FileHash -Algorithm SHA256 $PSItem | Select-Object hash | Format-Table -HideTableHeaders | Out-String) > (-join($PSItem,".sha256"))} RELEASE_FLAGS: --flag --static --flag -g --flag -fbacktrace --flag -O3 @@ -24,6 +24,8 @@ __Note:__ On Linux and MacOS, you will need to enable executable permission befo _e.g._ `$ chmod u+x fpm-v0.1.0-linux-x86_64` +__Github actions:__ to setup *fpm* within Github actions for automated testing, you can use the [fortran-lang/setup-fpm](https://github.com/marketplace/actions/setup-fpm) action. + For other platforms and architectures have a look at the [bootstrapping instructions](#bootstrapping-instructions). ### Creating a new project diff --git a/ci/run_tests.bat b/ci/run_tests.bat index 533590d..22be2db 100755 --- a/ci/run_tests.bat +++ b/ci/run_tests.bat @@ -36,6 +36,9 @@ if errorlevel 1 exit 1 .\build\gfortran_debug\app\hello_world if errorlevel 1 exit 1 +%fpm_path% run +if errorlevel 1 exit 1 + cd ..\hello_fpm if errorlevel 1 exit 1 @@ -71,6 +74,9 @@ del /q /f build %fpm_path% build if errorlevel 1 exit 1 +%fpm_path% test +if errorlevel 1 exit 1 + .\build\gfortran_debug\app\say_Hello if errorlevel 1 exit 1 diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 3588012..85484e5 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -23,6 +23,7 @@ rm -rf ./*/build cd hello_world "${f_fpm_path}" build ./build/gfortran_debug/app/hello_world +"${f_fpm_path}" run cd ../hello_fpm "${f_fpm_path}" build @@ -36,6 +37,7 @@ cd ../circular_example cd ../hello_complex "${f_fpm_path}" build +"${f_fpm_path}" test ./build/gfortran_debug/app/say_Hello ./build/gfortran_debug/app/say_goodbye ./build/gfortran_debug/test/greet_test @@ -24,9 +24,56 @@ sort: permission-alpha favicon: doc/media/favicon.ico print_creation_date: true extra_mods: iso_fortran_env:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html + tomlf:https://toml-f.github.io/toml-f + M_CLI2:https://github.com/urbanjost/M_CLI2 creation_date: %Y-%m-%d %H:%M %z md_extensions: markdown.extensions.toc markdown.extensions.smarty --- [TOC] + +# Fortran package manager developer documentation + +This is the main documentation of the Fortran package manager (*fpm*). +This document serves as developer documentation of *fpm* itself and contains general advice for developing in the *fpm* code base. + + +## The package manifest + +The central object describing an *fpm* project is the package manifest ``fpm.toml``. +The manifest is written in TOML, you can find the TOML specification at the official [TOML homepage](https://toml.io). + +The ``fpm.toml`` file targets project developers and maintainers to relieve them from writing build files for their packages. +With the package manifest a central place to collect information about the project is provided. +It contains the versioning and licensing meta data, as well as the information on external dependencies and the required build-tools or compiler settings. + +The manifest format specific to *fpm* projects is documented in the [manifest reference](page/Manifest.html). + +@Note For a more practical but less complete guide on creating *fpm* projects see the [packaging guide](page/Packaging.html). + +The details of the TOML parsing are implemented with using the [tomlf](https://toml-f.github.io/toml-f) module. +Generally, the interface to all TOML related functions for *fpm* is found in the proxy module [[fpm_toml]]. + +All the manifest types are bundled in [[fpm_manifest]]. +While the specific subtables for the package configuration are found in the ``src/fpm/manifest`` directory, they should be reexported in the [[fpm_manifest]] module if they should be elsewhere in *fpm*. + + +## Command line interface + +*fpm* is mainly used as a command line tool. +To work with an *fpm* project as a user you can completely rely on the command line. + +The command line interface is build with the [M_CLI2](https://github.com/urbanjost/M_CLI2) module and can be found in [[fpm_command_line]]. + + +## Generating this documentation + +This documentation is generated by [FORD](https://github.com/Fortran-FOSS-Programmers/FORD). +For more details on the [project file](https://github.com/fortran-lang/fpm/docs.md) and the comment markup in the source code visit the [FORD documentation](https://github.com/Fortran-FOSS-Programmers/ford/wiki). + +To regenerate this documentation run: + +```shell +ford docs.md +``` diff --git a/example_packages/hello_complex_2/app/say_hello/app_extra_mod.f90 b/example_packages/hello_complex_2/app/say_hello/app_extra_mod.f90 new file mode 100644 index 0000000..5059e22 --- /dev/null +++ b/example_packages/hello_complex_2/app/say_hello/app_extra_mod.f90 @@ -0,0 +1,6 @@ +module app_extra_mod +implicit none + +character(len=5) :: greet_object = "World" + +end module app_extra_mod diff --git a/example_packages/hello_complex_2/app/say_hello/app_hello_mod.f90 b/example_packages/hello_complex_2/app/say_hello/app_hello_mod.f90 index c5795cb..e44edd7 100644 --- a/example_packages/hello_complex_2/app/say_hello/app_hello_mod.f90 +++ b/example_packages/hello_complex_2/app/say_hello/app_hello_mod.f90 @@ -1,4 +1,5 @@ module app_hello_mod +use app_extra_mod, only: greet_object implicit none integer :: hello_int = 42 diff --git a/example_packages/hello_complex_2/app/say_hello/say_Hello.f90 b/example_packages/hello_complex_2/app/say_hello/say_Hello.f90 index 3b69ba7..3ebaebb 100644 --- a/example_packages/hello_complex_2/app/say_hello/say_Hello.f90 +++ b/example_packages/hello_complex_2/app/say_hello/say_Hello.f90 @@ -1,8 +1,8 @@ program say_Hello use greet_m, only: make_greeting - use app_hello_mod + use app_hello_mod, only: greet_object implicit none - print *, make_greeting("World") + print *, make_greeting(greet_object) end program say_Hello diff --git a/fpm/fpm.toml b/fpm/fpm.toml index 66e5049..70c9603 100644 --- a/fpm/fpm.toml +++ b/fpm/fpm.toml @@ -1,5 +1,5 @@ name = "fpm" -version = "0.1.1" +version = "0.1.2" license = "MIT" author = "fpm maintainers" maintainer = "" diff --git a/fpm/src/fpm.f90 b/fpm/src/fpm.f90 index 67be1cc..e1a322e 100644 --- a/fpm/src/fpm.f90 +++ b/fpm/src/fpm.f90 @@ -175,6 +175,7 @@ subroutine build_model(model, settings, package, error) allocate(package_list(1)) package_list(1)%s = package%name + if(settings%compiler.eq.'')then model%fortran_compiler = 'gfortran' else diff --git a/fpm/src/fpm/toml.f90 b/fpm/src/fpm/toml.f90 index 34f7c58..2e1d6d3 100644 --- a/fpm/src/fpm/toml.f90 +++ b/fpm/src/fpm/toml.f90 @@ -1,4 +1,4 @@ -!> Interface to TOML processing library. +!># Interface to TOML processing library !> !> This module acts as a proxy to the `toml-f` public Fortran API and allows !> to selectively expose components from the library to `fpm`. @@ -10,7 +10,8 @@ !> This module allows to implement features necessary for `fpm`, which are !> not yet available in upstream `toml-f`. !> -!> For more details on the library used see: https://toml-f.github.io/toml-f +!> For more details on the library used see the +!> [TOML-Fortran](https://toml-f.github.io/toml-f) developer pages. module fpm_toml use fpm_error, only : error_t, fatal_error, file_not_found_error use fpm_strings, only : string_t diff --git a/fpm/src/fpm_command_line.f90 b/fpm/src/fpm_command_line.f90 index 2a44a4f..7fac111 100644 --- a/fpm/src/fpm_command_line.f90 +++ b/fpm/src/fpm_command_line.f90 @@ -1,3 +1,27 @@ +!># Definition of the command line interface +!> +!> This module uses [M_CLI2](https://github.com/urbanjost/M_CLI2) to define +!> the command line interface. +!> To define a command line interface create a new command settings type +!> from the [[fpm_cmd_settings]] base class or the respective parent command +!> settings. +!> +!> The subcommand is selected by the first non-option argument in the command +!> line. In the subcase block the actual command line is defined and transferred +!> to an instance of the [[fpm_cmd_settings]], the actual type is used by the +!> *fpm* main program to determine which command entry point is chosen. +!> +!> To add a new subcommand add a new case to select construct and specify the +!> wanted command line and the expected default values. +!> Some of the following points also apply if you add a new option or argument +!> to an existing *fpm* subcommand. +!> Add this point you should create a help page for the new command in a simple +!> catman-like format as well in the ``set_help`` procedure. +!> Make sure to register new subcommands in the ``fpm-manual`` command by adding +!> them to the manual character array and in the help/manual case as well. +!> You should add the new command to the synopsis section of the ``fpm-list``, +!> ``fpm-help`` and ``fpm --list`` help pages below to make sure the help output +!> is complete and consistent as well. module fpm_command_line use fpm_environment, only : get_os_type, get_env, & OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & @@ -87,7 +111,7 @@ contains case default ; os_type = "OS Type: UNKNOWN" end select version_text = [character(len=80) :: & - & 'Version: 0.1.1, alpha', & + & 'Version: 0.1.2, alpha', & & 'Program: fpm(1)', & & 'Description: A Fortran package manager and build system', & & 'Home Page: https://github.com/fortran-lang/fpm', & diff --git a/fpm/src/fpm_targets.f90 b/fpm/src/fpm_targets.f90 index 03996f7..f9bbcda 100644 --- a/fpm/src/fpm_targets.f90 +++ b/fpm/src/fpm_targets.f90 @@ -259,12 +259,14 @@ function find_module_dependency(targets,module_name,include_dir) result(target_p end function find_module_dependency -!> For link targets, enumerate any dependency objects required for linking +!> For libraries and executables, build a list of objects required for linking +!> +!> stored in `target%link_objects` +!> subroutine resolve_target_linking(targets) type(build_target_ptr), intent(inout), target :: targets(:) - integer :: i,j,k - type(string_t) :: link_object + integer :: i do i=1,size(targets) @@ -272,47 +274,66 @@ subroutine resolve_target_linking(targets) allocate(target%link_objects(0)) - do j=1,size(target%dependencies) - - if (target%target_type == FPM_TARGET_ARCHIVE ) then - - ! Construct object list for archive - link_object%s = target%dependencies(j)%ptr%output_file - target%link_objects = [target%link_objects, link_object] - - else if (target%target_type == FPM_TARGET_EXECUTABLE .and. & - target%dependencies(j)%ptr%target_type == FPM_TARGET_OBJECT) then - - associate(exe_obj => target%dependencies(j)%ptr) - - ! Construct object list for executable - link_object%s = exe_obj%output_file - target%link_objects = [target%link_objects, link_object] - - ! Include non-library object dependencies - do k=1,size(exe_obj%dependencies) - - if (allocated(exe_obj%dependencies(k)%ptr%source)) then - if (exe_obj%dependencies(k)%ptr%source%unit_scope == & - exe_obj%source%unit_scope) then + if (target%target_type == FPM_TARGET_ARCHIVE) then - link_object%s = exe_obj%dependencies(k)%ptr%output_file - target%link_objects = [target%link_objects, link_object] + call get_link_objects(target%link_objects,target,is_exe=.false.) - end if - end if - - end do + else if (target%target_type == FPM_TARGET_EXECUTABLE) then - end associate - - end if + call get_link_objects(target%link_objects,target,is_exe=.true.) + + end if - end do end associate end do +contains + + !> Wrapper to build link object list + !> + !> For libraries: just list dependency objects of lib target + !> + !> For executables: need to recursively discover non-library + !> dependency objects. (i.e. modules in same dir as program) + !> + recursive subroutine get_link_objects(link_objects,target,is_exe) + type(string_t), intent(inout), allocatable :: link_objects(:) + type(build_target_t), intent(in) :: target + logical, intent(in) :: is_exe + + integer :: i + type(string_t) :: temp_str + + if (.not.allocated(target%dependencies)) return + + do i=1,size(target%dependencies) + + associate(dep => target%dependencies(i)%ptr) + + if (.not.allocated(dep%source)) cycle + + ! Skip library dependencies for executable targets + ! since the library archive will always be linked + if (is_exe.and.(dep%source%unit_scope == FPM_SCOPE_LIB)) cycle + + ! Skip if dependency object already listed + if (dep%output_file .in. link_objects) cycle + + ! Add dependency object file to link object list + temp_str%s = dep%output_file + link_objects = [link_objects, temp_str] + + ! For executable objects, also need to include non-library + ! dependencies from dependencies (recurse) + if (is_exe) call get_link_objects(link_objects,dep,is_exe=.true.) + + end associate + + end do + + end subroutine get_link_objects + end subroutine resolve_target_linking diff --git a/fpm/test/fpm_test/test_module_dependencies.f90 b/fpm/test/fpm_test/test_module_dependencies.f90 index 18929ac..5d78e0c 100644 --- a/fpm/test/fpm_test/test_module_dependencies.f90 +++ b/fpm/test/fpm_test/test_module_dependencies.f90 @@ -1,14 +1,15 @@ !> Define tests for the `fpm_sources` module (module dependency checking) module test_module_dependencies use testsuite, only : new_unittest, unittest_t, error_t, test_failed - use fpm_targets, only: targets_from_sources, resolve_module_dependencies + use fpm_targets, only: targets_from_sources, resolve_module_dependencies, & + resolve_target_linking use fpm_model, only: fpm_model_t, srcfile_t, build_target_t, build_target_ptr, & 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_TARGET_EXECUTABLE, FPM_TARGET_OBJECT, FPM_TARGET_ARCHIVE - use fpm_strings, only: string_t + use fpm_strings, only: string_t, operator(.in.) implicit none private @@ -71,14 +72,16 @@ contains if (allocated(error)) then return end if - if (size(model%targets) /= 3) then call test_failed(error,'Incorrect number of model%targets - expecting three') return end if + call resolve_target_linking(model%targets) + call check_target(model%targets(1)%ptr,type=FPM_TARGET_ARCHIVE,n_depends=2, & - deps = [model%targets(2),model%targets(3)],error=error) + deps = [model%targets(2),model%targets(3)], & + links = model%targets(2:3), error=error) if (allocated(error)) return @@ -146,8 +149,10 @@ contains return end if + call resolve_target_linking(model%targets) + call check_target(model%targets(1)%ptr,type=FPM_TARGET_ARCHIVE,n_depends=1, & - deps=[model%targets(2)],error=error) + deps=[model%targets(2)],links=[model%targets(2)],error=error) if (allocated(error)) return @@ -162,7 +167,8 @@ contains if (allocated(error)) return call check_target(model%targets(4)%ptr,type=FPM_TARGET_EXECUTABLE,n_depends=2, & - deps=[model%targets(1),model%targets(3)],error=error) + deps=[model%targets(1),model%targets(3)], & + links=[model%targets(3)], error=error) if (allocated(error)) return @@ -202,20 +208,22 @@ contains return end if + call resolve_target_linking(model%targets) + call check_target(model%targets(1)%ptr,type=FPM_TARGET_OBJECT,n_depends=0, & source=sources(1),error=error) if (allocated(error)) return call check_target(model%targets(2)%ptr,type=FPM_TARGET_EXECUTABLE,n_depends=1, & - deps=[model%targets(1)],error=error) + deps=[model%targets(1)],links=[model%targets(1)],error=error) if (allocated(error)) return end subroutine test_program_with_module - !> Check program using a module in same directory + !> Check program using modules in same directory subroutine test_program_own_module_use(error) !> Error handling @@ -233,7 +241,7 @@ contains integer, intent(in) :: exe_scope type(error_t), allocatable, intent(out) :: error - type(srcfile_t) :: sources(2) + type(srcfile_t) :: sources(3) type(fpm_model_t) :: model character(:), allocatable :: scope_str @@ -241,13 +249,17 @@ contains scope_str = merge('FPM_SCOPE_APP ','FPM_SCOPE_TEST',exe_scope==FPM_SCOPE_APP)//' - ' - sources(1) = new_test_source(FPM_UNIT_MODULE,file_name="app/app_mod.f90", & + sources(1) = new_test_source(FPM_UNIT_MODULE,file_name="app/app_mod1.f90", & scope = exe_scope, & - provides=[string_t('app_mod')]) + provides=[string_t('app_mod1')]) - sources(2) = new_test_source(FPM_UNIT_PROGRAM,file_name="app/my_program.f90", & + sources(2) = new_test_source(FPM_UNIT_MODULE,file_name="app/app_mod2.f90", & + scope = exe_scope, & + provides=[string_t('app_mod2')],uses=[string_t('app_mod1')]) + + sources(3) = new_test_source(FPM_UNIT_PROGRAM,file_name="app/my_program.f90", & scope=exe_scope, & - uses=[string_t('app_mod')]) + uses=[string_t('app_mod2')]) call targets_from_sources(model,sources) call resolve_module_dependencies(model%targets,error) @@ -256,11 +268,12 @@ contains return end if - if (size(model%targets) /= 3) then + if (size(model%targets) /= 4) then call test_failed(error,scope_str//'Incorrect number of model%targets - expecting three') return end if + call resolve_target_linking(model%targets) call check_target(model%targets(1)%ptr,type=FPM_TARGET_OBJECT,n_depends=0, & source=sources(1),error=error) @@ -272,11 +285,16 @@ contains if (allocated(error)) return - call check_target(model%targets(3)%ptr,type=FPM_TARGET_EXECUTABLE,n_depends=1, & - deps=[model%targets(2)],error=error) + call check_target(model%targets(3)%ptr,type=FPM_TARGET_OBJECT,n_depends=1, & + source=sources(3),deps=[model%targets(2)],error=error) + + if (allocated(error)) return + + call check_target(model%targets(4)%ptr,type=FPM_TARGET_EXECUTABLE,n_depends=1, & + deps=[model%targets(3)],links=model%targets(1:3), error=error) if (allocated(error)) return - + end subroutine test_scope end subroutine test_program_own_module_use @@ -414,12 +432,13 @@ contains !> Helper to check an expected output target - subroutine check_target(target,type,n_depends,deps,source,error) + subroutine check_target(target,type,n_depends,deps,links,source,error) type(build_target_t), intent(in) :: target integer, intent(in) :: type integer, intent(in) :: n_depends type(srcfile_t), intent(in), optional :: source type(build_target_ptr), intent(in), optional :: deps(:) + type(build_target_ptr), intent(in), optional :: links(:) type(error_t), intent(out), allocatable :: error integer :: i @@ -448,6 +467,34 @@ contains end if + if (present(links)) then + + do i=1,size(links) + + if (.not.(links(i)%ptr%output_file .in. target%link_objects)) then + call test_failed(error,'Missing object ('//links(i)%ptr%output_file//& + ') for executable "'//target%output_file//'"') + return + end if + + end do + + if (size(links) > size(target%link_objects)) then + + call test_failed(error,'There are missing link objects for target "'& + //target%output_file//'"') + return + + elseif (size(links) < size(target%link_objects)) then + + call test_failed(error,'There are more link objects than expected for target "'& + //target%output_file//'"') + return + + end if + + end if + if (present(source)) then if (allocated(target%source)) then |