diff options
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | ci/run_tests.bat | 2 | ||||
-rwxr-xr-x | ci/run_tests.sh | 3 | ||||
-rw-r--r-- | fpm/.gitignore | 1 | ||||
-rw-r--r-- | fpm/fpm.toml | 7 | ||||
-rw-r--r-- | fpm/src/fpm.f90 | 26 | ||||
-rw-r--r-- | fpm/src/fpm/cmd/new.f90 | 153 | ||||
-rw-r--r-- | fpm/src/fpm_command_line.f90 | 758 | ||||
-rw-r--r-- | fpm/test/cli_test/cli_test.f90 | 14 | ||||
-rw-r--r-- | fpm/test/new_test/new_test.f90 | 155 |
10 files changed, 733 insertions, 388 deletions
@@ -77,7 +77,7 @@ with the following contents and initialized as a git repository. * `fpm test` – run tests The command `fpm run` can optionally accept the name of the specific executable -to run, as can `fpm test`; like `fpm run specifc_executable`. Command line +to run, as can `fpm test`; like `fpm run specific_executable`. Command line arguments can also be passed to the executable(s) or test(s) with the option `--args "some arguments"`. diff --git a/ci/run_tests.bat b/ci/run_tests.bat index 2a68f53..0c0339c 100755 --- a/ci/run_tests.bat +++ b/ci/run_tests.bat @@ -9,8 +9,10 @@ if errorlevel 1 exit 1 fpm run if errorlevel 1 exit 1 +rmdir fpm_scratch_* /s /q fpm test if errorlevel 1 exit 1 +rmdir fpm_scratch_* /s /q for /f %%i in ('where /r build fpm.exe') do set fpm_path=%%i diff --git a/ci/run_tests.sh b/ci/run_tests.sh index df7fb24..625f37b 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -17,10 +17,11 @@ set -ex cd fpm fpm build fpm run +rm -rf fpm_scratch_*/ fpm test +rm -rf fpm_scratch_*/ f_fpm_path="$(get_abs_filename $(find build -regex 'build/.*/app/fpm'))" - "${f_fpm_path}" cd ../example_packages/hello_world diff --git a/fpm/.gitignore b/fpm/.gitignore index c602557..a007fea 100644 --- a/fpm/.gitignore +++ b/fpm/.gitignore @@ -1,2 +1 @@ build/* -*/FODDER/* diff --git a/fpm/fpm.toml b/fpm/fpm.toml index 9418204..fc3a381 100644 --- a/fpm/fpm.toml +++ b/fpm/fpm.toml @@ -20,6 +20,13 @@ source-dir = "test/cli_test" main = "cli_test.f90" [[test]] +name = "new-test" +source-dir = "test/new_test" +main = "new_test.f90" + +[[test]] name = "fpm-test" source-dir = "test/fpm_test" main = "main.f90" + + diff --git a/fpm/src/fpm.f90 b/fpm/src/fpm.f90 index 36ee766..575b654 100644 --- a/fpm/src/fpm.f90 +++ b/fpm/src/fpm.f90 @@ -320,7 +320,7 @@ logical :: list stop endif else - !! expand names, duplicates are a problem?? + !*! expand names, duplicates are a problem?? allocate(foundit(size(settings%name))) foundit=.false. FINDIT: do i=1,size(package%executable) @@ -335,18 +335,15 @@ logical :: list do i=1,size(settings%name) if(.not.foundit(i))then write(stderr,'(*(g0,1x))')'fpm::run<ERROR>:executable',trim(settings%name(i)),'not located' - !!elseif(settings%debug)then - !! write(stderr,'(*(g0,1x))')'fpm::run<INFO>:executable',trim(settings%name(i)),'located at',newwords(i),& - !! & merge('exists ','does not exist',exists(trim(settings%name(i)))) endif enddo if(allocated(foundit))deallocate(foundit) endif do i=1,size(newwords) - !! list is a new option for use with xargs, to move files to production area, valgrind, gdb, ls -l, .... - !! maybe add as --mask and could do --mask 'echo %xx' or --mask 'cp %XX /usr/local/bin/' an so on - !! default if blank would be filename uptodate|needs|updated|doesnotexist creation_date, ... - !! or maybe just list filenames so can pipe through xargs, and so on + !*! list is a new option for use with xargs, to move files to production area, valgrind, gdb, ls -l, .... + !*! maybe add as --mask and could do --mask 'echo %xx' or --mask 'cp %XX /usr/local/bin/' an so on + !*! default if blank would be filename uptodate|needs|updated|doesnotexist creation_date, ... + !*! or maybe just list filenames so can pipe through xargs, and so on if(settings%list)then write(stderr,'(*(g0,1x))')'fpm::run<INFO>:executable expected at',newwords(i),& & merge('exists ','does not exist',exists(newwords(i))) @@ -405,7 +402,7 @@ logical :: list stop endif else - !! expand names, duplicates are a problem?? + !*! expand names, duplicates are a problem?? allocate(foundit(size(settings%name))) foundit=.false. FINDIT: do i=1,size(package%test) @@ -420,18 +417,15 @@ logical :: list do i=1,size(settings%name) if(.not.foundit(i))then write(stderr,'(*(g0,1x))')'fpm::run<ERROR>:test',trim(settings%name(i)),'not located' - !!elseif(settings%debug)then - !! write(stderr,'(*(g0,1x))')'fpm::run<INFO>:test',trim(settings%name(i)),'located at',newwords(i),& - !! & merge('exists ','does not exist',exists(trim(settings%name(i)))) endif enddo if(allocated(foundit))deallocate(foundit) endif do i=1,size(newwords) - !! list is a new option for use with xargs, to move files to production area, valgrind, gdb, ls -l, .... - !! maybe add as --mask and could do --mask 'echo %xx' or --mask 'cp %XX /usr/local/bin/' an so on - !! default if blank would be filename uptodate|needs|updated|doesnotexist creation_date, ... - !! or maybe just list filenames so can pipe through xargs, and so on + !*! list is a new option for use with xargs, to move files to production area, valgrind, gdb, ls -l, .... + !*! maybe add as --mask and could do --mask 'echo %xx' or --mask 'cp %XX /usr/local/bin/' an so on + !*! default if blank would be filename uptodate|needs|updated|doesnotexist creation_date, ... + !*! or maybe just list filenames so can pipe through xargs, and so on if(settings%list)then write(stderr,'(*(g0,1x))')'fpm::run<INFO>:test expected at',newwords(i),& & merge('exists ','does not exist',exists(newwords(i))) diff --git a/fpm/src/fpm/cmd/new.f90 b/fpm/src/fpm/cmd/new.f90 index fc4c93e..91145d8 100644 --- a/fpm/src/fpm/cmd/new.f90 +++ b/fpm/src/fpm/cmd/new.f90 @@ -2,7 +2,7 @@ module fpm_cmd_new use fpm_command_line, only : fpm_new_settings use fpm_environment, only : run, OS_LINUX, OS_MACOS, OS_WINDOWS -use fpm_filesystem, only : join_path, exists, basename, mkdir +use fpm_filesystem, only : join_path, exists, basename, mkdir, is_dir use,intrinsic :: iso_fortran_env, only : stderr=>error_unit implicit none private @@ -10,43 +10,66 @@ public :: cmd_new contains -subroutine cmd_new(settings) ! --with-executable F --with-test F ' +subroutine cmd_new(settings) type(fpm_new_settings), intent(in) :: settings character(len=:),allocatable :: bname ! baeename of NAME character(len=:),allocatable :: message(:) character(len=:),allocatable :: littlefile(:) +character(len=8) :: date + + call date_and_time(DATE=date) + + if(exists(settings%name) .and. .not.settings%backfill )then + write(stderr,'(*(g0,1x))')& + & 'ERROR: ',settings%name,'already exists.' + write(stderr,'(*(g0,1x))')& + & ' perhaps you wanted to add --backfill ?' + return + elseif(is_dir(settings%name) .and. settings%backfill )then + write(*,'(*(g0))')'backfilling ',settings%name + elseif(exists(settings%name) )then + write(stderr,'(*(g0,1x))')& + & 'ERROR: ',settings%name,'already exists and is not a directory.' + return + else + ! make new directory + call mkdir(settings%name) + endif - call mkdir(settings%name) ! make new directory - call run('cd '//settings%name) ! change to new directory as a test. System dependent potentially - !! NOTE: need some system routines to handle filenames like "." like realpath() or getcwd(). + ! change to new directory as a test. System dependent potentially + call run('cd '//settings%name) + !*! NOTE: need some system routines to handle filenames like "." + !*! like realpath() or getcwd(). bname=basename(settings%name) - !! weird gfortran bug?? lines truncated to concatenated string length, not 80 - !! hit some weird gfortran bug when littlefile data was an argument to warnwrite(3f), ok when a variable - - call warnwrite(join_path(settings%name, '.gitignore'), ['build/*']) ! create NAME/.gitignore file + ! create NAME/.gitignore file + call warnwrite(join_path(settings%name, '.gitignore'), ['build/*']) littlefile=[character(len=80) :: '# '//bname, 'My cool new project!'] - call warnwrite(join_path(settings%name, 'README.md'), littlefile) ! create NAME/README.md - - message=[character(len=80) :: & ! start building NAME/fpm.toml - &'name = "'//bname//'" ', & - &'version = "0.1.0" ', & - &'license = "license" ', & - &'author = "Jane Doe" ', & - &'maintainer = "jane.doe@example.com" ', & - &'copyright = "2020 Jane Doe" ', & - &' ', & + ! create NAME/README.md + call warnwrite(join_path(settings%name, 'README.md'), littlefile) + + ! start building NAME/fpm.toml + message=[character(len=80) :: & + &'name = "'//bname//'" ', & + &'version = "0.1.0" ', & + &'license = "license" ', & + &'author = "Jane Doe" ', & + &'maintainer = "jane.doe@example.com" ', & + &'copyright = "'//date(1:4)//' Jane Doe" ', & + &' ', & &''] if(settings%with_lib)then call mkdir(join_path(settings%name,'src') ) - message=[character(len=80) :: message, & ! create next section of fpm.toml + ! create next section of fpm.toml + message=[character(len=80) :: message, & &'[library] ', & &'source-dir="src" ', & &''] - littlefile=[character(len=80) :: & ! create placeholder module src/bname.f90 + ! create placeholder module src/bname.f90 + littlefile=[character(len=80) :: & &'module '//bname, & &' implicit none', & &' private', & @@ -57,52 +80,67 @@ character(len=:),allocatable :: littlefile(:) &' print *, "Hello, '//bname//'!"', & &' end subroutine say_hello', & &'end module '//bname] - ! a proposed alternative default - call warnwrite(join_path(settings%name, 'src', bname//'.f90'), littlefile) ! create NAME/src/NAME.f90 + ! create NAME/src/NAME.f90 + call warnwrite(join_path(settings%name, 'src', bname//'.f90'),& + & littlefile) endif if(settings%with_test)then - call mkdir(join_path(settings%name, 'test')) ! create NAME/test or stop - message=[character(len=80) :: message, & ! create next section of fpm.toml + + ! create NAME/test or stop + call mkdir(join_path(settings%name, 'test')) + ! create next section of fpm.toml + message=[character(len=80) :: message, & &'[[test]] ', & &'name="runTests" ', & &'source-dir="test" ', & &'main="main.f90" ', & &''] - littlefile=[character(len=80) :: & + littlefile=[character(len=80) :: & &'program main', & &'implicit none', & &'', & &'print *, "Put some tests in here!"', & &'end program main'] - ! a proposed alternative default a little more substantive - call warnwrite(join_path(settings%name, 'test/main.f90'), littlefile) ! create NAME/test/main.f90 + ! create NAME/test/main.f90 + call warnwrite(join_path(settings%name, 'test/main.f90'), littlefile) endif if(settings%with_executable)then - call mkdir(join_path(settings%name, 'app')) ! create NAME/app or stop - message=[character(len=80) :: message, & ! create next section of fpm.toml + ! create next section of fpm.toml + call mkdir(join_path(settings%name, 'app')) + ! create NAME/app or stop + message=[character(len=80) :: message, & &'[[executable]] ', & &'name="'//bname//'" ', & &'source-dir="app" ', & &'main="main.f90" ', & &''] - littlefile=[character(len=80) :: & - &'program main', & - &' use '//bname//', only: say_hello', & - &'', & - &' implicit none', & - &'', & - &' call say_hello', & - &'end program main'] + if(exists(bname//'/src/'))then + littlefile=[character(len=80) :: & + &'program main', & + &' use '//bname//', only: say_hello', & + &' implicit none', & + &'', & + &' call say_hello()', & + &'end program main'] + else + littlefile=[character(len=80) :: & + &'program main', & + &' implicit none', & + &'', & + &' print *, "hello from project '//bname//'"', & + &'end program main'] + endif call warnwrite(join_path(settings%name, 'app/main.f90'), littlefile) endif - call warnwrite(join_path(settings%name, 'fpm.toml'), message) ! now that built it write NAME/fpm.toml - - call run('cd ' // settings%name // '&&git init') ! assumes these commands work on all systems and git(1) is installed + ! now that built it write NAME/fpm.toml + call warnwrite(join_path(settings%name, 'fpm.toml'), message) + ! assumes git(1) is installed and in path + call run('git init ' // settings%name) contains subroutine warnwrite(fname,data) @@ -110,16 +148,19 @@ character(len=*),intent(in) :: fname character(len=*),intent(in) :: data(:) if(.not.exists(fname))then - call filewrite(fname,data) + call filewrite(fname,data) else - write(stderr,'(*(g0,1x))')'fpm::new<WARNING>',fname,'already exists. Not overwriting' + write(stderr,'(*(g0,1x))')'INFO: ',fname,& + & 'already exists. Not overwriting' endif end subroutine warnwrite subroutine filewrite(filename,filedata) -use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit -! write filedata to file filename +! procedure to write filedata to file filename +use,intrinsic :: iso_fortran_env, only : & + & stdin=>input_unit, stdout=>output_unit, stderr=>error_unit + character(len=*),intent(in) :: filename character(len=*),intent(in) :: filedata(:) integer :: lun, i, ios @@ -130,11 +171,11 @@ character(len=256) :: message if(filename.ne.' ')then open(file=filename, & & newunit=lun, & - & form='formatted', & ! FORM = FORMATTED | UNFORMATTED - & access='sequential', & ! ACCESS = SEQUENTIAL | DIRECT | STREAM - & action='write', & ! ACTION = READ|WRITE | READWRITE - & position='rewind', & ! POSITION = ASIS | REWIND | APPEND - & status='new', & ! STATUS = NEW | REPLACE | OLD | SCRATCH | UNKNOWN + & form='formatted', & ! FORM = FORMATTED | UNFORMATTED + & access='sequential', & ! ACCESS = SEQUENTIAL| DIRECT | STREAM + & action='write', & ! ACTION = READ|WRITE| READWRITE + & position='rewind', & ! POSITION= ASIS | REWIND | APPEND + & status='new', & ! STATUS = NEW| REPLACE| OLD| SCRATCH| UNKNOWN & iostat=ios, & & iomsg=message) else @@ -142,17 +183,21 @@ character(len=256) :: message ios=0 endif if(ios.ne.0)then - write(stderr,'(*(a:,1x))')'*filewrite* error:',filename,trim(message) + write(stderr,'(*(a:,1x))')& + & '*filewrite* error:',filename,trim(message) error stop 1 endif - do i=1,size(filedata) ! write file + ! write file + do i=1,size(filedata) write(lun,'(a)',iostat=ios,iomsg=message)trim(filedata(i)) if(ios.ne.0)then - write(stderr,'(*(a:,1x))')'*filewrite* error:',filename,trim(message) + write(stderr,'(*(a:,1x))')& + & '*filewrite* error:',filename,trim(message) error stop 4 endif enddo - close(unit=lun,iostat=ios,iomsg=message) ! close file + ! close file + close(unit=lun,iostat=ios,iomsg=message) if(ios.ne.0)then write(stderr,'(*(a:,1x))')'*filewrite* error:',trim(message) error stop 2 diff --git a/fpm/src/fpm_command_line.f90 b/fpm/src/fpm_command_line.f90 index 1a7e4ab..cf7c761 100644 --- a/fpm/src/fpm_command_line.f90 +++ b/fpm/src/fpm_command_line.f90 @@ -3,10 +3,11 @@ use fpm_environment, only : get_os_type, & OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, & OS_CYGWIN, OS_SOLARIS, OS_FREEBSD use M_CLI2, only : set_args, lget, unnamed, remaining, specified -use fpm_filesystem, only : basename +use fpm_strings, only : lower +use fpm_filesystem, only : basename, canon_path use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, & - & stdout=>output_unit, & - & stderr=>error_unit + & stdout=>output_unit, & + & stderr=>error_unit implicit none private @@ -27,6 +28,7 @@ type, extends(fpm_cmd_settings) :: fpm_new_settings logical :: with_executable=.false. logical :: with_test=.false. logical :: with_lib=.true. + logical :: backfill=.true. end type type, extends(fpm_cmd_settings) :: fpm_build_settings @@ -52,11 +54,14 @@ type, extends(fpm_cmd_settings) :: fpm_install_settings end type character(len=:),allocatable :: name +character(len=:),allocatable :: os_type character(len=ibug),allocatable :: names(:) character(len=:), allocatable :: version_text(:) -character(len=:), allocatable :: help_new(:), help_fpm(:), help_run(:), help_test(:), help_build(:) -character(len=:), allocatable :: help_text(:), help_install(:), help_help(:) +character(len=:), allocatable :: help_new(:), help_fpm(:), help_run(:), & + & help_test(:), help_build(:), help_usage(:), & + & help_text(:), help_install(:), help_help(:), & + & help_list(:), help_list_dash(:), help_list_nodash(:) contains subroutine get_command_line_settings(cmd_settings) @@ -64,24 +69,37 @@ contains character(len=4096) :: cmdarg integer :: i + integer :: widest call set_help() ! text for --version switch, + select case (get_os_type()) + case (OS_LINUX); os_type = "OS Type: Linux" + case (OS_MACOS); os_type = "OS Type: macOS" + case (OS_WINDOWS); os_type = "OS Type: Windows" + 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_UNKNOWN); os_type = "OS Type: Unknown" + case default ; os_type = "OS Type: UNKNOWN" + end select version_text = [character(len=80) :: & - & 'Version: 0.1.0, Pre-alpha', & - & 'Program: fpm(1)', & - & 'Description: A Fortran package manager and build system', & - & 'Home Page: https://github.com/fortran-lang/fpm', & - & 'License: MIT', & - & ''] - ! find the subcommand name by looking for first word on command not starting with dash + & 'Version: 0.1.0, Pre-alpha', & + & 'Program: fpm(1)', & + & 'Description: A Fortran package manager and build system', & + & 'Home Page: https://github.com/fortran-lang/fpm', & + & 'License: MIT', & + & os_type] + ! find the subcommand name by looking for first word on command + ! not starting with dash cmdarg = '' do i = 1, command_argument_count() call get_command_argument(i, cmdarg) if(adjustl(cmdarg(1:1)) .ne. '-')exit enddo - ! now set subcommand-specific help text and process commandline arguments. Then call subcommand routine + ! now set subcommand-specific help text and process commandline + ! arguments. Then call subcommand routine select case(trim(cmdarg)) case('run') @@ -101,51 +119,52 @@ contains call set_args( '--release F --list F --',help_build,version_text ) allocate( fpm_build_settings :: cmd_settings ) - cmd_settings=fpm_build_settings( release=lget('release'),list=lget('list') ) + cmd_settings=fpm_build_settings( release=lget('release'), & + & list=lget('list') ) case('new') - call set_args(' --with-executable F --with-test F --lib F --app F --test F', help_new, version_text) + call set_args(' --src F --lib F --app F --test F --backfill F', & + & help_new, version_text) select case(size(unnamed)) case(1) - write(stderr,'(*(g0))')'ERROR: directory name required' - write(stderr,'(*(g0))')' usage: fpm new NAME [--with-executable] [--with-test]' + write(stderr,'(*(g0,/))')'ERROR: directory name required' + write(stderr,'(*(7x,g0,/))') & + & 'USAGE: fpm new NAME [--lib|--src] [--app] [--test] [--backfill]' stop 1 case(2) name=trim(unnamed(2)) case default - write(stderr,'(*(g0))')'ERROR: only one directory name allowed' - write(stderr,'(*(g0))')' usage: fpm new NAME [--with-executable] [--with-test]' + write(stderr,'(g0)')'ERROR: only one directory name allowed' + write(stderr,'(7x,g0)') & + & 'USAGE: fpm new NAME [--lib|--src] [--app] [--test] [--backfill]' stop 2 end select - + !*! canon_path is not converting ".", etc. + name=canon_path(name) if( .not.is_fortran_name(basename(name)) )then - write(stderr,'(*(g0))')'ERROR: the new directory basename must be an allowed Fortran name.' - write(stderr,'(*(g0))')' It must be composed of 1 to 63 ASCII characters and start' - write(stderr,'(*(g0))')' with a letter and be composed entirely of alphanumeric' - write(stderr,'(*(g0))')' characters [A-Za-z0-9] and underscores.' + write(stderr,'(g0)') [ character(len=72) :: & + & 'ERROR: the new directory basename must be an allowed ', & + & ' Fortran name. It must be composed of 1 to 63 ASCII', & + & ' characters and start with a letter and be composed', & + & ' entirely of alphanumeric characters [a-zA-Z0-9]', & + & ' and underscores.'] stop 4 endif allocate(fpm_new_settings :: cmd_settings) - cmd_settings=fpm_new_settings(name=name, & - & with_executable=lget('with-executable'), & - & with_test=lget('with-test'), & - & with_lib=.true.) - - ! use alternative --lib --test --app switches. In production pick one - ! method or probably an error should be using --with and one of these - if (any( specified(['lib ','app ','test']) ) )then - if (any( specified(['with-executable','with-test ']) ) )then - write(stderr,'(*(g0))') 'A BIT FROWARD:' - write(stderr,'(*(g0))') ' DO NOT MIX --with-* with [--lib|--app|--test]' - write(stderr,'(*(g0))') ' THEY ARE TWO DIFFERENT PROTOTYPE PROPOSALS ' - write(stderr,'(*(g0))') ' SEE ISSUES #111 #110 #109' - write(stderr,'(*(g0))') ' START WITH https://github.com/fortran-lang/fpm/issues/111' - endif + + if (any( specified(['src ','lib ','app ','test']) ) )then + cmd_settings=fpm_new_settings(name=name, & + & with_executable=lget('app'), & + & with_test=lget('test'), & + & with_lib=any([lget('lib'),lget('src')]), & + & backfill=lget('backfill') ) + else cmd_settings=fpm_new_settings(name=name, & - & with_executable=lget('app'), & - & with_test=lget('test'), & - & with_lib=lget('lib') ) + & with_executable=.true., & + & with_test=.true., & + & with_lib=.true., & + & backfill=lget('backfill') ) endif case('help') @@ -153,41 +172,52 @@ contains if(size(unnamed).lt.2)then unnamed=['help', 'fpm '] endif - allocate(character(len=80) :: help_text(0)) + widest=256 + allocate(character(len=widest) :: help_text(0)) do i=2,size(unnamed) select case(unnamed(i)) case('build ' ) - help_text=[character(len=80) :: help_text, help_build] + help_text=[character(len=widest) :: help_text, help_build] case('run ' ) - help_text=[character(len=80) :: help_text, help_run] + help_text=[character(len=widest) :: help_text, help_run] case('help ' ) - help_text=[character(len=80) :: help_text, help_help] + help_text=[character(len=widest) :: help_text, help_help] case('test ' ) - help_text=[character(len=80) :: help_text, help_test] + help_text=[character(len=widest) :: help_text, help_test] case('new ' ) - help_text=[character(len=80) :: help_text, help_new] + help_text=[character(len=widest) :: help_text, help_new] case('fpm ' ) - help_text=[character(len=80) :: help_text, help_fpm] + help_text=[character(len=widest) :: help_text, help_fpm] + case('list ' ) + help_text=[character(len=widest) :: help_text, help_list] case('version' ) - help_text=[character(len=80) :: help_text, version_text] - case('all ' ) - help_text=[character(len=80) :: help_text, help_fpm] - help_text=[character(len=80) :: help_text, help_new] - help_text=[character(len=80) :: help_text, help_build] - help_text=[character(len=80) :: help_text, help_run] - help_text=[character(len=80) :: help_text, help_test] - help_text=[character(len=80) :: help_text, version_text] + help_text=[character(len=widest) :: help_text, version_text] + case('manual ' ) + help_text=[character(len=widest) :: help_text, help_fpm] + help_text=[character(len=widest) :: help_text, help_new] + help_text=[character(len=widest) :: help_text, help_build] + help_text=[character(len=widest) :: help_text, help_run] + help_text=[character(len=widest) :: help_text, help_test] + help_text=[character(len=widest) :: help_text, help_help] + help_text=[character(len=widest) :: help_text, help_list] + help_text=[character(len=widest) :: help_text, version_text] case default - help_text=[character(len=80) :: help_text, 'unknown subcommand'//unnamed(i)] + help_text=[character(len=widest) :: help_text, & + & 'ERROR: unknown help topic "'//trim(unnamed(i))//'"'] end select enddo - write(stderr,'(g0)')(trim(help_text(i)), i=1, size(help_text) ) + call printhelp(help_text) case('install') call set_args('--release F ', help_install, version_text) allocate(fpm_install_settings :: cmd_settings) - + case('list') + call set_args(' --list F', help_list, version_text) + call printhelp(help_list_nodash) + if(lget('list'))then + call printhelp(help_list_dash) + endif case('test') call set_args('--list F --release F --',help_test,version_text) @@ -203,24 +233,34 @@ contains case default - call set_args(' ', help_fpm, version_text) - ! Note: will not get here if --version or --usage or --help is present on commandline - if(len_trim(cmdarg).eq.0)then - write(stderr,'(*(a))')'ERROR: missing subcommand' + call set_args(' --list F', help_fpm, version_text) + ! Note: will not get here if --version or --usage or --help + ! is present on commandline + help_text=help_usage + if(lget('list'))then + help_text=help_list_dash + elseif(len_trim(cmdarg).eq.0)then + write(stdout,'(*(a))')'Fortran Package Manager:' + write(stdout,'(*(a))')' ' + call printhelp(help_list_nodash) else - write(stderr,'(*(a))')'ERROR: unknown subcommand [', trim(cmdarg), ']' + write(stderr,'(*(a))')'ERROR: unknown subcommand [', & + & trim(cmdarg), ']' + call printhelp(help_list_dash) endif - help_text=[character(len=80) :: & - 'USAGE: fpm [ SUBCOMMAND [SUBCOMMAND_OPTIONS] ] | [|--help|--version] ', & - ' Enter "fpm --help" for more information ', & - '' ] - write(stderr,'(g0)')(trim(help_text(i)), i=1, size(help_text) ) + call printhelp(help_text) end select + contains + subroutine printhelp(lines) + character(len=:),intent(in),allocatable :: lines(:) + write(stdout,'(g0)')(trim(lines(i)), i=1, size(lines) ) + end subroutine printhelp end subroutine get_command_line_settings function is_fortran_name(line) result (lout) - ! determine if a string is a valid Fortran name ignoring trailing spaces (but not leading spaces) + ! determine if a string is a valid Fortran name ignoring trailing spaces + ! (but not leading spaces) character(len=*),parameter :: int='0123456789' character(len=*),parameter :: lower='abcdefghijklmnopqrstuvwxyz' character(len=*),parameter :: upper='ABCDEFGHIJKLMNOPQRSTUVWXYZ' @@ -240,255 +280,357 @@ contains end function is_fortran_name subroutine set_help() + help_list_nodash=[character(len=80) :: & + 'USAGE: fpm [ SUBCOMMAND [SUBCOMMAND_OPTIONS] ]|[--list|--help|--version]', & + ' where SUBCOMMAND is commonly new|build|run|test ', & + ' ', & + ' subcommand may be one of ', & + ' ', & + ' build Compile the package placing results in the "build" directory', & + ' help Display help ', & + ' list Display this list of subcommand descriptions ', & + ' new Create a new Fortran package directory with sample files ', & + ' run Run the local package application programs ', & + ' test Run the test programs ', & + ' ', & + ' Enter "fpm --list" for a brief list of subcommand options. Enter ', & + ' "fpm --help" or "fpm SUBCOMMAND --help" for detailed descriptions. ', & + ' '] + help_list_dash = [character(len=80) :: & + ' ', & + ' build [--release] [--list] ', & + ' help [NAME(s)] ', & + ' new NAME [--lib|--src] [--app] [--test] [--backfill] ', & + ' list [--list] ', & + ' run [NAME(s)] [--release] [--list] [-- ARGS] ', & + ' test [NAME(s)] [--release] [--list] [-- ARGS] ', & + ' '] + help_usage=[character(len=80) :: & + '' ] help_fpm=[character(len=80) :: & - 'NAME', & - ' fpm(1) - A Fortran package manager and build system', & - 'OS TYPE' ] - select case (get_os_type()) - case (OS_LINUX); help_fpm=[character(len=80) :: help_fpm, " Linux" ] - case (OS_MACOS); help_fpm=[character(len=80) :: help_fpm, " macOS" ] - case (OS_WINDOWS); help_fpm=[character(len=80) :: help_fpm, " Windows" ] - case (OS_CYGWIN); help_fpm=[character(len=80) :: help_fpm, " Cygwin" ] - case (OS_SOLARIS); help_fpm=[character(len=80) :: help_fpm, " Solaris" ] - case (OS_FREEBSD); help_fpm=[character(len=80) :: help_fpm, " FreeBSD" ] - case (OS_UNKNOWN); help_fpm=[character(len=80) :: help_fpm, " Unknown" ] - case default ; help_fpm=[character(len=80) :: help_fpm, " UNKNOWN" ] - end select - help_fpm=[character(len=80) :: help_fpm, & - 'SYNOPSIS ', & - ' fpm SUBCOMMAND [SUBCOMMAND_OPTIONS] ', & - ' ', & - ' fpm --help|--version ', & - ' ', & - 'DESCRIPTION ', & - ' fpm is a package manager that helps you create Fortran projects that are ', & - ' optionally dependent on multiple files and other fpm(1) packages. ', & - ' ', & - ' Most significantly fpm(1) lets you pull upon other fpm(1) packages in ', & - ' distributed git(1) repositories as if the packages were a basic part ', & - ' of your default programming environment, as well as letting you share ', & - ' your projects with others in a similar manner. ', & - ' ', & - ' See the fpm(1) repository for a listing of such available projects. ', & - ' ', & - ' All output goes into the directory "build/". ', & - ' ', & - 'SUBCOMMANDS ', & - ' Valid fpm subcommands are: ', & - ' ', & - ' build [--release] [--list] ', & - ' Compile the packages into the "build/" directory. ', & - ' new NAME [--with-executable] [--with-test] ', & - ' Create a new Fortran package directory ', & - ' with sample files ', & - ' run [NAME(s)] [--release] [--list] [-- ARGS] ', & - ' Run the local package binaries. defaults to all ', & - ' binaries for that release. ', & - ' test [NAME(s)] [--release] [--list] [-- ARGS] ', & - ' Run the tests ', & - ' help [NAME(s)] Alternate method for displaying subcommand help ', & - ' ', & - 'SUBCOMMAND OPTIONS ', & - ' --release Builds or runs in release mode (versus debug mode). fpm(1) ', & - ' Defaults to using common compiler debug flags and building ', & - ' in "build/gfortran_debug/". When this flag is present build ', & - ' output goes into "build/gfortran_release/" and common ', & - ' compiler optimization flags are used. ', & - ' --list list candidates instead of building or running them ', & - ' -- ARGS Arguments to pass to executables/tests ', & - ' --help Show this help text and exit. Valid for all subcommands. ', & - ' --version Show version information and exit. Valid for all subcommands.', & - 'EXAMPLES ', & - ' sample commands: ', & - ' ', & - ' fpm build ', & - ' fpm test ', & - ' fpm run ', & - ' fpm new --help ', & - ' fpm new mypackage --with-executable --with-test ', & - ' fpm run myprogram --release -- -x 10 -y 20 --title "my title" ', & - 'SEE ALSO ', & - ' The fpm(1) home page is https://github.com/fortran-lang/fpm ', & + 'NAME ', & + ' fpm(1) - A Fortran package manager and build system ', & + ' ', & + 'SYNOPSIS ', & + ' fpm SUBCOMMAND [SUBCOMMAND_OPTIONS] ', & + ' ', & + ' fpm --help|--version|--list ', & + ' ', & + 'DESCRIPTION ', & + ' fpm(1) is a package manager that helps you create Fortran projects ', & + ' from source. ', & + ' ', & + ' Most significantly fpm(1) lets you draw upon other fpm(1) packages ', & + ' in distributed git(1) repositories as if the packages were a basic ', & + ' part of your default programming environment, as well as letting ', & + ' you share your projects with others in a similar manner. ', & + ' ', & + ' See the fpm(1) repository at https://fortran-lang.org/packages/fpm ', & + ' for a listing of registered projects. ', & + ' ', & + ' All output goes into the directory "build/" which can generally be ', & + ' removed and rebuilt if required. Note that if external packages are ', & + ' being used you need network connectivity to rebuild from scratch. ', & + ' ', & + 'SUBCOMMANDS ', & + ' Valid fpm(1) subcommands are: ', & + ' ', & + ' build [--release] [--list] ', & + ' Compile the packages into the "build/" directory. ', & + ' new NAME [--lib|--src] [--app] [--test] [--backfill] ', & + ' Create a new Fortran package directory ', & + ' with sample files ', & + ' run [NAME(s)] [--release] [--list] [-- ARGS] ', & + ' Run the local package binaries. defaults to all ', & + ' binaries for that release. ', & + ' test [NAME(s)] [--release] [--list] [-- ARGS] ', & + ' Run the tests ', & + ' help [NAME(s)] Alternate method for displaying subcommand help ', & + ' list [--list] Display brief descriptions of all subcommands. ', & + ' ', & + 'SUBCOMMAND OPTIONS ', & + ' --release Builds or runs in release mode (versus debug mode). fpm(1)', & + ' Defaults to using common compiler debug flags and building', & + ' in "build/*_debug/". When this flag is present build ', & + ' output goes into "build/*_release/" and common compiler ', & + ' optimization flags are used. ', & + ' --list List candidates instead of building or running them. On ', & + ' the fpm(1) command this shows a brief list of subcommands.', & + ' -- ARGS Arguments to pass to executables. ', & + ' --help Show help text and exit. Valid for all subcommands. ', & + ' --version Show version information and exit. Valid for all ', & + ' subcommands. ', & + ' ', & + 'EXAMPLES ', & + ' sample commands: ', & + ' ', & + ' fpm new mypackage --app --test ', & + ' fpm build ', & + ' fpm test ', & + ' fpm run ', & + ' fpm new --help ', & + ' fpm run myprogram --release -- -x 10 -y 20 --title "my title" ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & ''] + help_list=[character(len=80) :: & + 'NAME ', & + ' list(1) - list summary of fpm(1) subcommands ', & + ' ', & + 'SYNOPSIS ', & + ' fpm list [-list] ', & + ' ', & + ' fpm list --help|--version ', & + ' ', & + 'DESCRIPTION ', & + ' Display a short description for each fpm(1) subcommand. ', & + ' ', & + 'OPTIONS ', & + ' --list display a list of command options as well. This is the ', & + ' same output as generated by "fpm --list". ', & + ' ', & + 'EXAMPLES ', & + ' display a short list of fpm(1) subcommands ', & + ' ', & + ' fpm list ', & + ' fpm --list ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & + '' ] help_run=[character(len=80) :: & - 'NAME ', & - ' run(1) - the fpm(1) subcommand to run project applications ', & - ' ', & - 'SYNOPSIS ', & - ' fpm run [NAME(s)] [--release] [-- ARGS] ', & - ' ', & - ' fpm run --help|--version ', & - ' ', & - 'DESCRIPTION ', & - ' Run applications you have built in your fpm(1) project. ', & - ' ', & - 'OPTIONS ', & - ' 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. ', & - ' --release selects the optimized build instead of the debug ', & - ' build. ', & - ' --list list candidates instead of building or running them', & - ' -- ARGS optional arguments to pass to the program(s). ', & - ' The same arguments are passed to all names ', & - ' specified. ', & - ' ', & - 'EXAMPLES ', & - 'run fpm(1) project applications ', & - ' ', & - ' # run default programs in /app or as specified in "fpm.toml" ', & - ' fpm run ', & - ' ', & - ' # run a specific program and pass arguments to the command ', & - ' fpm run mytest -- -x 10 -y 20 -title "my title line" ', & - ' ', & - ' # production version of two applications ', & - ' fpm run tst1 test2 -release ', & - 'SEE ALSO ', & - ' The fpm(1) home page is https://github.com/fortran-lang/fpm ', & + 'NAME ', & + ' run(1) - the fpm(1) subcommand to run project applications ', & + ' ', & + 'SYNOPSIS ', & + ' fpm run [NAME(s)] [--release] [-- ARGS] ', & + ' ', & + ' fpm run --help|--version ', & + ' ', & + 'DESCRIPTION ', & + ' Run applications you have built in your fpm(1) project. ', & + ' ', & + 'OPTIONS ', & + ' 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. ', & + ' --release selects the optimized build instead of the debug ', & + ' build. ', & + ' --list list candidates instead of building or running them ', & + ' -- ARGS optional arguments to pass to the program(s). ', & + ' The same arguments are passed to all names ', & + ' specified. ', & + ' ', & + 'EXAMPLES ', & + ' run fpm(1) project applications ', & + ' ', & + ' # run default programs in /app or as specified in "fpm.toml" ', & + ' fpm run ', & + ' ', & + ' # run a specific program and pass arguments to the command ', & + ' fpm run mytest -- -x 10 -y 20 --title "my title line" ', & + ' ', & + ' # run production version of two applications ', & + ' fpm run prg1 prg2 --release ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & '' ] help_build=[character(len=80) :: & - 'NAME ', & - ' build(1) - the fpm(1) subcommand to build a project ', & - 'SYNOPSIS ', & - ' fpm build [--release]|[-list] ', & - ' fpm build --help|--version ', & - ' ', & - 'DESCRIPTION ', & - ' The "fpm build" command ', & - ' o Fetches any dependencies ', & - ' o Scans your sources ', & - ' o Builds them in the proper order ', & - ' ', & - ' The Fortran source files are assumed to be in app/, test/, and src/ ', & - ' by default. The changed or new files found are rebuilt. ', & - ' The results are placed in the build/ directory. ', & - ' ', & - ' Non-default pathnames and remote dependencies are used if ', & - ' specified in the "fpm.toml" file. ', & - ' ', & - 'OPTIONS ', & - ' --release build in build/*_release instead of build/*_debug with ', & - ' high optimization instead of full debug options. ', & - ' --list list candidates instead of building or running them ', & - ' --help print this help and exit ', & - ' --version print program version information and exit ', & - ' ', & - 'EXAMPLES ', & - ' Sample commands: ', & - ' ', & - ' fpm build # build with debug options ', & - ' fpm build -release # build with high optimization ', & - 'SEE ALSO ', & - ' The fpm(1) home page is https://github.com/fortran-lang/fpm ', & + 'NAME ', & + ' build(1) - the fpm(1) subcommand to build a project ', & + ' ', & + 'SYNOPSIS ', & + ' fpm build [--release]|[-list] ', & + ' ', & + ' fpm build --help|--version ', & + ' ', & + 'DESCRIPTION ', & + ' The "fpm build" command ', & + ' o Fetches any dependencies ', & + ' o Scans your sources ', & + ' o Builds them in the proper order ', & + ' ', & + ' The Fortran source files are assumed by default to be in ', & + ' o src/ for modules and procedure source ', & + ' o app/ main program(s) for applications ', & + ' o test/ main program(s) and support files for project tests ', & + ' Changed or new files found are rebuilt. The results are placed in ', & + ' the build/ directory. ', & + ' ', & + ' Non-default pathnames and remote dependencies are used if ', & + ' specified in the "fpm.toml" file. ', & + ' ', & + 'OPTIONS ', & + ' --release build in build/*_release instead of build/*_debug with ', & + ' high optimization instead of full debug options. ', & + ' --list list candidates instead of building or running them ', & + ' --help print this help and exit ', & + ' --version print program version information and exit ', & + ' ', & + 'EXAMPLES ', & + ' Sample commands: ', & + ' ', & + ' fpm build # build with debug options ', & + ' fpm build --release # build with high optimization ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & '' ] + help_help=[character(len=80) :: & - 'NAME ', & - ' help(1) - the fpm(1) subcommand to display help ', & - 'SYNOPSIS ', & - ' fpm help [fpm][new][build][run][test][help][version][all] ', & - ' ', & - 'DESCRIPTION ', & - ' The "fpm help" command is an alternative to the --help parameter ', & - ' on the fpm(1) command and its subcommands. ', & - ' ', & - 'OPTIONS ', & - ' NAME(s) A list of topic names to display. ', & - ' ', & - 'EXAMPLES ', & - ' ', & - ' fpm help ', & - ' fpm help version ', & - ' fpm all ', & - 'SEE ALSO ', & - ' The fpm(1) home page is https://github.com/fortran-lang/fpm ', & + 'NAME ', & + ' help(1) - the fpm(1) subcommand to display help ', & + ' ', & + 'SYNOPSIS ', & + ' fpm help [fpm] [new] [build] [run] [test] [help] [version] [manual] ', & + ' ', & + 'DESCRIPTION ', & + ' The "fpm help" command is an alternative to the --help parameter ', & + ' on the fpm(1) command and its subcommands. ', & + ' ', & + 'OPTIONS ', & + ' NAME(s) A list of topic names to display. All the subcommands ', & + ' have their own page (new, build, run, test, ...). ', & + ' ', & + ' The special name "manual" displays all the fpm(1) ', & + ' built-in documentation. ', & + ' ', & + ' The default is to display help for the fpm(1) command ', & + ' itself. ', & + ' ', & + 'EXAMPLES ', & + ' Sample usage: ', & + ' ', & + ' fpm help # general fpm(1) command help ', & + ' fpm help version # show program version ', & + ' fpm help new # display help for "new" subcommand ', & + ' fpm help manual # All fpm(1) built-in documentation ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & '' ] - help_new=[character(len=80) :: & - 'NAME ', & - ' new(1) - the fpm(1) subcommand to initialize a new project ', & - 'SYNOPSIS ', & - ' fpm new NAME [--with-executable] [--with-test] ', & - ' ', & - ' fpm new --help|--version ', & - ' ', & - 'DESCRIPTION ', & - ' Create a new programming project in a new directory ', & - ' ', & - ' The "new" subcommand creates a directory and runs the command ', & - ' "git init" in that directory and makes an example "fpm.toml" ', & - ' file. and src/ directory and a sample module file. It ', & - ' optionally also creates a test/ and app/ directory with ', & - ' trivial example Fortran program sources. ', & - ' ', & - ' Remember to update the information in the sample "fpm.toml" ', & - ' file with such information as your name and e-mail address. ', & - ' ', & - 'OPTIONS ', & - ' NAME the name of the project directory to create. The name ', & - ' must be a valid Fortran name composed of 1 to 63 ', & - ' ASCII alphanumeric characters and underscores, ', & - ' starting with a letter. ', & - ' --with-executable additionally create optional directory app/ ', & - ' and placeholder program for "fpm run". ', & - ' --with-test additionally create optional directory test/', & - ' and placeholder program for "fpm test". ', & - ' --help print this help and exit ', & - ' --version print program version information and exit ', & - ' ', & - 'EXAMPLES ', & - ' Sample use ', & - ' ', & - ' fpm new myproject # create new project directory and seed it', & - ' cd myproject # Enter the new directory ', & - ' # and run commands such as ', & - ' fpm build ', & - ' fpm run # if you selected --with-executable ', & - ' fpm test # if you selected --with-test ', & - 'SEE ALSO ', & - ' The fpm(1) home page is https://github.com/fortran-lang/fpm ', & - ' ', & - ' Registered packages are at https://fortran-lang.org/packages ', & + help_new=[character(len=80) :: & + 'NAME ', & + ' new(1) - the fpm(1) subcommand to initialize a new project ', & + 'SYNOPSIS ', & + ' fpm new NAME [--lib|--src] [--app] [--test] [--backfill] ', & + ' ', & + ' fpm new --help|--version ', & + ' ', & + 'DESCRIPTION ', & + ' "fpm new" creates and populates a new programming project directory. ', & + ' It ', & + ' o creates a directory with the specified name ', & + ' o runs the command "git init" in that directory ', & + ' o populates the directory with the default project directories ', & + ' o adds sample Fortran source files ', & + ' o adds a ".gitignore" file for ignoring the build/ directory ', & + ' (where fpm-generated output will be placed) ', & + ' ', & + ' The basic default file structure is ', & + ' ', & + ' NAME/ ', & + ' fpm.toml ', & + ' .gitignore ', & + ' src/ ', & + ' NAME.f90 ', & + ' app/ ', & + ' main.f90 ', & + ' test/ ', & + ' main.f90 ', & + ' ', & + ' Remember to update the information in the sample "fpm.toml" ', & + ' file with your name and e-mail address. ', & + ' ', & + 'OPTIONS ', & + ' NAME the name of the project directory to create. The name ', & + ' must be a valid Fortran name composed of 1 to 63 ', & + ' ASCII alphanumeric characters and underscores, ', & + ' starting with a letter. ', & + ' ', & + ' The default is to create all of the src/, app/, and test/ ', & + ' directories. If any of the following options are specified ', & + ' then only selected subdirectories are generated: ', & + ' ', & + ' --lib,--src create directory src/ and a placeholder module ', & + ' named "NAME.f90" for use with subcommand "build". ', & + ' --app create directory app/ and a placeholder main ', & + ' program for use with subcommand "run". ', & + ' --test create directory test/ and a placeholder program ', & + ' for use with the subcommand "test". Note that sans ', & + ' "--lib" it really does not have anything to test. ', & + ' ', & + ' So the default is equivalent to "fpm NAME --lib --app --test". ', & + ' ', & + ' --backfill By default the directory must not exist. If this ', & + ' option is present the directory may pre-exist and ', & + ' only subdirectories and files that do not ', & + ' already exist will be created. For example, if you ', & + ' previously entered "fpm new myname --lib" entering ', & + ' "fpm new myname --backfill" will create the missing ', & + ' app/ and test/ directories and programs. ', & + ' ', & + ' --help print this help and exit ', & + ' --version print program version information and exit ', & + ' ', & + 'EXAMPLES ', & + ' Sample use ', & + ' ', & + ' fpm new myproject # create new project directory and seed it ', & + ' cd myproject # Enter the new directory ', & + ' # and run commands such as ', & + ' fpm build ', & + ' fpm run # run example application program ', & + ' fpm test # run example test program ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & + ' ', & + ' Registered packages are at https://fortran-lang.org/packages ', & '' ] help_test=[character(len=80) :: & - 'NAME ', & - ' test(1) - the fpm(1) subcommand to run project tests ', & - ' ', & - 'SYNOPSIS ', & - ' fpm test [NAME(s)] [--release] [--list] [-- ARGS] ', & - ' ', & - ' fpm test --help|--version ', & - ' ', & - 'DESCRIPTION ', & - ' Run applications you have built to test your project. ', & - ' ', & - 'OPTIONS ', & - ' NAME(s) optional list of specific test names to execute. ', & - ' The default is to run all the tests in test/ ', & - ' or the tests listed in the "fpm.toml" file. ', & - ' --release selects the optimized build instead of the debug ', & - ' build. ', & - ' --list list candidates instead of building or running them', & - ' -- ARGS optional arguments to pass to the test program(s). ', & - ' The same arguments are passed to all test names ', & - ' specified. ', & - ' ', & - 'EXAMPLES ', & - 'run tests ', & - ' ', & - ' # run default tests in /test or as specified in "fpm.toml" ', & - ' fpm test ', & - ' ', & - ' # run a specific test and pass arguments to the command ', & - ' fpm test mytest -- -x 10 -y 20 -title "my title line" ', & - ' ', & - ' fpm test tst1 test2 -release # production version of two tests', & - 'SEE ALSO ', & - ' The fpm(1) home page is https://github.com/fortran-lang/fpm ', & + 'NAME ', & + ' test(1) - the fpm(1) subcommand to run project tests ', & + ' ', & + 'SYNOPSIS ', & + ' fpm test [NAME(s)] [--release] [--list] [-- ARGS] ', & + ' ', & + ' fpm test --help|--version ', & + ' ', & + 'DESCRIPTION ', & + ' Run applications you have built to test your project. ', & + ' ', & + 'OPTIONS ', & + ' NAME(s) optional list of specific test names to execute. ', & + ' The default is to run all the tests in test/ ', & + ' or the tests listed in the "fpm.toml" file. ', & + ' --release selects the optimized build instead of the debug ', & + ' build. ', & + ' --list list candidates instead of building or running them ', & + ' -- ARGS optional arguments to pass to the test program(s). ', & + ' The same arguments are passed to all test names ', & + ' specified. ', & + ' ', & + 'EXAMPLES ', & + 'run tests ', & + ' ', & + ' # run default tests in /test or as specified in "fpm.toml" ', & + ' fpm test ', & + ' ', & + ' # run a specific test and pass arguments to the command ', & + ' fpm test mytest -- -x 10 -y 20 --title "my title line" ', & + ' ', & + ' fpm test tst1 tst2 --release # production version of two tests ', & + ' ', & + 'SEE ALSO ', & + ' The fpm(1) home page at https://github.com/fortran-lang/fpm ', & '' ] help_install=[character(len=80) :: & - ' fpm(1) subcommand "install" ', & - ' ', & - ' Usage: fpm install NAME ', & + ' fpm(1) subcommand "install" ', & + ' ', & + ' USAGE: fpm install NAME ', & '' ] end subroutine set_help diff --git a/fpm/test/cli_test/cli_test.f90 b/fpm/test/cli_test/cli_test.f90 index b0140e1..59f1f7a 100644 --- a/fpm/test/cli_test/cli_test.f90 +++ b/fpm/test/cli_test/cli_test.f90 @@ -41,11 +41,11 @@ character(len=*),parameter :: tests(*)= [ character(len=256) :: & 'CMD="new", ESTAT=1,', & !'CMD="new -unknown", ESTAT=2,', & -'CMD="new my_project another yet_another -with-test", ESTAT=2,', & -'CMD="new my_project --with-executable", W_E=T, NAME="my_project",', & -'CMD="new my_project --with-executable -with-test", W_E=T,W_T=T, NAME="my_project",', & -'CMD="new my_project -with-test", W_T=T, NAME="my_project",', & -'CMD="new my_project", NAME="my_project",', & +'CMD="new my_project another yet_another -test", ESTAT=2,', & +'CMD="new my_project --app", W_E=T, NAME="my_project",', & +'CMD="new my_project --app --test", W_E=T,W_T=T, NAME="my_project",', & +'CMD="new my_project --test", W_T=T, NAME="my_project",', & +'CMD="new my_project", W_E=T,W_T=T, NAME="my_project",', & 'CMD="run", ', & 'CMD="run my_project", NAME="my_project", ', & @@ -91,8 +91,8 @@ if(command_argument_count().eq.0)then ! assume if called with no arguments to d ! blank out name group EXPECTED name=[(repeat(' ',len(name)),i=1,max_names)] ! the words on the command line sans the subcommand name release=.false. ! --release - w_e=.false. ! --with-executable - w_t=.false. ! --with-test + w_e=.false. ! --app + w_t=.false. ! --test args=repeat(' ',132) ! -- ARGS cmd=repeat(' ',132) ! the command line arguments to test cstat=0 ! status values from EXECUTE_COMMAND_LINE() diff --git a/fpm/test/new_test/new_test.f90 b/fpm/test/new_test/new_test.f90 new file mode 100644 index 0000000..8007f7a --- /dev/null +++ b/fpm/test/new_test/new_test.f90 @@ -0,0 +1,155 @@ +program new_test +use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit +use fpm_filesystem, only : is_dir, list_files, exists, windows_path +use fpm_strings, only : string_t, operator(.in.) +use fpm_environment, only : run, get_os_type +use fpm_environment, only : OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_WINDOWS +implicit none +type(string_t), allocatable :: file_names(:) +integer :: i, j, k +character(len=*),parameter :: cmdpath = 'build/gfortran_debug/app/fpm' +character(len=:),allocatable :: path +character(len=*),parameter :: scr = 'fpm_scratch_' +character(len=*),parameter :: cmds(*) = [character(len=80) :: & +! run a variety of "fpm new" variations and verify expected files are generated +' new', & +' new no-no', & +' new '//scr//'A', & +' new '//scr//'B --lib', & +' new '//scr//'C --app', & +' new '//scr//'D --test', & +' new '//scr//'E --lib --test ', & +' new '//scr//'F --lib --app', & +' new '//scr//'G --test --app', & +' new '//scr//'BB --lib', & +' new '//scr//'BB --test ', & +' new '//scr//'BB --backfill --test', & +' new '//scr//'CC --test --src --app', & +' new --version', & +' new --help'] +integer :: estat, cstat +character(len=256) :: message +character(len=:),allocatable :: directories(:) +character(len=:),allocatable :: shortdirs(:) +character(len=:),allocatable :: expected(:) +logical,allocatable :: tally(:) +logical :: IS_OS_WINDOWS + write(*,'(g0:,1x)')'TEST new SUBCOMMAND (draft):' + allocate(tally(0)) + shortdirs=[character(len=80) :: 'A','B','C','D','E','F','G','BB','CC'] + allocate(character(len=80) :: directories(size(shortdirs))) + + !! SEE IF EXPECTED FILES ARE GENERATED + !! Issues: + !! o assuming fpm command is in expected path and the new version + !! o DOS versus POSIX filenames + is_os_windows=.false. + select case (get_os_type()) + case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD) + call execute_command_line('rm -rf fpm_scratch_*',exitstat=estat,cmdstat=cstat,cmdmsg=message) + path=cmdpath + case (OS_WINDOWS) + path=windows_path(cmdpath) + is_os_windows=.true. + call execute_command_line('rmdir fpm_scratch_* /s /q',exitstat=estat,cmdstat=cstat,cmdmsg=message) + case default + write(*,*)'ERROR: unknown OS. Stopping test' + stop 2 + end select + do i=1,size(directories) + directories(i)=scr//trim(shortdirs(i)) + if( is_dir(trim(directories(i))) ) then + write(*,*)'ERROR:',trim( directories(i) ),' already exists' + write(*,*)' you must remove scratch directories before performing this test' + write(*,'(*(g0:,1x))')'directories:',(trim(directories(j)),j=1,size(directories)),'no-no' + stop + endif + enddo + ! execute the fpm(1) commands + do i=1,size(cmds) + message='' + write(*,*)path//' '//cmds(i) + call execute_command_line(path//' '//cmds(i),exitstat=estat,cmdstat=cstat,cmdmsg=message) + write(*,'(*(g0))')'CMD=',trim(cmds(i)),' EXITSTAT=',estat,' CMDSTAT=',cstat,' MESSAGE=',trim(message) + enddo + + if( is_dir('no-no') ) then + tally=[tally,.false.] + write(*,*)'ERROR: directory no-no/ exists' + else + tally=[tally,.true.] + endif + + ! assuming hidden files in .git and .gitignore are ignored for now + TESTS: do i=1,size(directories) + ! test if expected directory exists + if( .not. is_dir(trim( directories(i))) ) then + tally=[tally,.false.] + write(*,*)'ERROR:',trim( directories(i) ),' is not a directory' + else + select case(shortdirs(i)) + case('A'); expected=[ character(len=80)::& + &'A/app','A/fpm.toml','A/README.md','A/src','A/test','A/app/main.f90','A/src/'//scr//'A.f90','A/test/main.f90'] + case('B'); expected=[ character(len=80)::& + &'B/fpm.toml','B/README.md','B/src','B/src/'//scr//'B.f90'] + case('C'); expected=[ character(len=80)::& + &'C/app','C/fpm.toml','C/README.md','C/app/main.f90'] + case('D'); expected=[ character(len=80)::& + &'D/fpm.toml','D/README.md','D/test','D/test/main.f90'] + case('E'); expected=[ character(len=80)::& + &'E/fpm.toml','E/README.md','E/src','E/test','E/src/'//scr//'E.f90','E/test/main.f90'] + case('F'); expected=[ character(len=80)::& + &'F/app','F/fpm.toml','F/README.md','F/src','F/app/main.f90','F/src/'//scr//'F.f90'] + case('G'); expected=[ character(len=80)::& + &'G/app','G/fpm.toml','G/README.md','G/test','G/app/main.f90','G/test/main.f90'] + case('BB'); expected=[ character(len=80)::& + &'BB/fpm.toml','BB/README.md','BB/src','BB/test','BB/src/'//scr//'BB.f90','BB/test/main.f90'] + case('CC'); expected=[ character(len=80)::& + &'CC/app','CC/fpm.toml','CC/README.md','CC/src','CC/test','CC/app/main.f90','CC/src/'//scr//'CC.f90','CC/test/main.f90'] + case default + write(*,*)'ERROR: internal error. unknown directory name ',trim(shortdirs(i)) + stop 4 + end select + !! MSwindows has hidden files in it + !! Warning: This only looks for expected files. If there are more files than expected it does not fail + call list_files(trim(directories(i)), file_names,recurse=.true.) + + if(size(expected).ne.size(file_names))then + write(*,*)'WARNING: unexpected number of files in file list=',size(file_names),' expected ',size(expected) + write(*,'("EXPECTED: ",*(g0:,","))')(scr//trim(expected(j)),j=1,size(expected)) + write(*,'("FOUND: ",*(g0:,","))')(trim(file_names(j)%s),j=1,size(file_names)) + endif + + do j=1,size(expected) + + expected(j)=scr//expected(j) + if(is_os_windows) expected(j)=windows_path(expected(j)) + if( .not.(trim(expected(j)).in.file_names) )then + tally=[tally,.false.] + write(*,'("ERROR: FOUND ",*(g0:,", "))')( trim(file_names(k)%s), k=1,size(file_names) ) + write(*,'(*(g0))')' BUT NO MATCH FOR ',expected(j) + tally=[tally,.false.] + cycle TESTS + endif + enddo + tally=[tally,.true.] + endif + enddo TESTS + + ! clean up scratch files; might want an option to leave them for inspection + select case (get_os_type()) + case (OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_CYGWIN, OS_SOLARIS, OS_FREEBSD) + call execute_command_line('rm -rf fpm_scratch_*',exitstat=estat,cmdstat=cstat,cmdmsg=message) + case (OS_WINDOWS) + call execute_command_line('rmdir fpm_scratch_* /s /q',exitstat=estat,cmdstat=cstat,cmdmsg=message) + end select + + write(*,'("TALLY=",*(g0))')tally + if(all(tally))then + write(*,'(*(g0))')'PASSED: all ',count(tally),' tests passed ' + else + write(*,*)'FAILED: PASSED=',count(tally),' FAILED=',count(.not.tally) + stop 5 + endif + +end program new_test |