diff options
author | John S. Urban <urbanjost@comcast.net> | 2020-12-22 00:37:10 -0500 |
---|---|---|
committer | John S. Urban <urbanjost@comcast.net> | 2020-12-22 00:37:10 -0500 |
commit | 0fe6b1fe311ad43acb2ecd98fb2b98cf40636c95 (patch) | |
tree | c8eb1631574b47a6dffa413542a4a7948391acdc | |
parent | d435b766f2b3193eb077db0d2e8025daa29d55ef (diff) | |
download | fpm-0fe6b1fe311ad43acb2ecd98fb2b98cf40636c95.tar.gz fpm-0fe6b1fe311ad43acb2ecd98fb2b98cf40636c95.zip |
complicating new
-rw-r--r-- | fpm/src/fpm/cmd/new.f90 | 295 | ||||
-rw-r--r-- | fpm/src/fpm_command_line.f90 | 48 |
2 files changed, 281 insertions, 62 deletions
diff --git a/fpm/src/fpm/cmd/new.f90 b/fpm/src/fpm/cmd/new.f90 index fcdf6fd..9b0b362 100644 --- a/fpm/src/fpm/cmd/new.f90 +++ b/fpm/src/fpm/cmd/new.f90 @@ -65,17 +65,11 @@ contains subroutine cmd_new(settings) type(fpm_new_settings), intent(in) :: settings -character(len=:),allocatable :: bname ! baeename of NAME -character(len=:),allocatable :: tomlfile(:) -character(len=:),allocatable :: littlefile(:) -character(len=8) :: date +integer,parameter :: tfc = selected_char_kind('DEFAULT') +character(len=:,kind=tfc),allocatable :: bname ! baeename of NAME +character(len=:,kind=tfc),allocatable :: tomlfile(:) +character(len=:,kind=tfc),allocatable :: littlefile(:) - !> get date to put into metadata in manifest file "fpm.toml" - call date_and_time(DATE=date) - - write(*,*)settings%name - write(*,*)settings%backfill - write(*,*)settings%verbose !> TOP DIRECTORY NAME PROCESSING !> see if requested new directory already exists and process appropriately if(exists(settings%name) .and. .not.settings%backfill )then @@ -110,7 +104,7 @@ character(len=8) :: date call warnwrite(join_path(settings%name, 'README.md'), littlefile) ! start building NAME/fpm.toml - if(settings%verbose)then + if(settings%with_full)then tomlfile=[character(len=80) :: & &'# Manifest root ',& &' ',& @@ -161,22 +155,13 @@ character(len=8) :: date &' ',& &'## TARGETS ',& &''] - else - tomlfile=[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" ', & - &' ', & - &''] endif - if(settings%with_lib)then + if(settings%with_bare)then + elseif(settings%with_lib)then call mkdir(join_path(settings%name,'src') ) ! create next section of fpm.toml - if(settings%verbose)then + if(settings%with_full)then tomlfile=[character(len=80) :: tomlfile, & &'[library] ', & &'source-dir="src" ', & @@ -199,12 +184,13 @@ character(len=8) :: date & littlefile) endif - if(settings%with_test)then + if(settings%with_bare)then + elseif(settings%with_test)then ! create NAME/test or stop call mkdir(join_path(settings%name, 'test')) ! create next section of fpm.toml - if(settings%verbose)then + if(settings%with_full)then tomlfile=[character(len=80) :: tomlfile ,& &'[[test]] ', & &'name="runTests" ', & @@ -223,12 +209,13 @@ character(len=8) :: date call warnwrite(join_path(settings%name, 'test/check.f90'), littlefile) endif - if(settings%with_example)then + if(settings%with_bare)then + elseif(settings%with_example)then ! create NAME/example or stop call mkdir(join_path(settings%name, 'example')) ! create next section of fpm.toml - if(settings%verbose)then + if(settings%with_full)then tomlfile=[character(len=80) :: tomlfile, & &'[[example]] ', & &'name="demo" ', & @@ -247,11 +234,12 @@ character(len=8) :: date call warnwrite(join_path(settings%name, 'example/demo.f90'), littlefile) endif - if(settings%with_executable)then + if(settings%with_bare)then + elseif(settings%with_executable)then ! create next section of fpm.toml call mkdir(join_path(settings%name, 'app')) ! create NAME/app or stop - if(settings%verbose)then + if(settings%with_full)then tomlfile=[character(len=80) :: tomlfile, & &'[[executable]] ', & &'name="'//bname//'" ', & @@ -278,7 +266,8 @@ character(len=8) :: date endif call warnwrite(join_path(settings%name, 'app/main.f90'), littlefile) endif - if(settings%verbose)then + + if(settings%with_full)then tomlfile=[character(len=80) :: tomlfile, & &'[dependencies] ', & &'# ', & @@ -292,11 +281,55 @@ character(len=8) :: date endif ! now that built it write NAME/fpm.toml - call warnwrite(join_path(settings%name, 'fpm.toml'), tomlfile) + if( allocated(tomlfile) )then + call validate_toml_data(tomlfile) + call warnwrite(join_path(settings%name, 'fpm.toml'), tomlfile) + else + call create_verified_basic_manifest(join_path(settings%name, 'fpm.toml')) + endif ! assumes git(1) is installed and in path call run('git init ' // settings%name) contains +subroutine create_verified_basic_manifest(filename) +!> create a basic but verified default manifest file +use fpm_toml, only : toml_table, toml_serializer, set_value +use fpm_manifest_package, only : package_config_t, new_package +use fpm_error, only : error_t +implicit none +character(len=*),intent(in) :: filename + type(toml_table) :: table + type(toml_serializer) :: ser + type(package_config_t) :: package + type(error_t), allocatable :: error + integer :: lun + character(len=8) :: date + + !> get date to put into metadata in manifest file "fpm.toml" + call date_and_time(DATE=date) + table = toml_table() + ser = toml_serializer() + call fileopen(filename,lun) ! fileopen stops on error + + call set_value(table, "name", BNAME) + call set_value(table, "version", "0.1.0") + call set_value(table, "license", "license") + call set_value(table, "author", "Jane Doe") + call set_value(table, "maintainer", "jane.doe@example.com") + call set_value(table, "copyright", 'Copyright '//date(1:4)//', Jane Doe') + ! continue building of manifest + ! ... + call new_package(package, table, error) + if (allocated(error)) stop 3 + if(settings%verbose)then + call table%accept(ser) + endif + ser%unit=lun + call table%accept(ser) + call fileclose(lun) ! fileopen stops on error + +end subroutine create_verified_basic_manifest + subroutine warnwrite(fname,data) character(len=*),intent(in) :: fname character(len=*),intent(in) :: data(:) @@ -310,15 +343,15 @@ character(len=*),intent(in) :: data(:) end subroutine warnwrite -subroutine filewrite(filename,filedata) -! procedure to write filedata to file filename +subroutine fileopen(filename,lun) +! procedure to open 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 -character(len=256) :: message +character(len=*),intent(in) :: filename +integer,intent(out) :: lun +integer :: i, ios +character(len=256) :: message message=' ' ios=0 @@ -339,25 +372,187 @@ character(len=256) :: message if(ios.ne.0)then write(stderr,'(*(a:,1x))')& & '<ERROR> *filewrite*:',filename,trim(message) - error stop 1 + lun=-1 + stop 1 endif - ! write file - do i=1,size(filedata) - write(lun,'(a)',iostat=ios,iomsg=message)trim(filedata(i)) + +end subroutine fileopen + +subroutine fileclose(lun) +use,intrinsic :: iso_fortran_env, only : & + & stdin=>input_unit, stdout=>output_unit, stderr=>error_unit +integer,intent(in) :: lun +character(len=256) :: message +integer :: ios + if(lun.ne.-1)then + close(unit=lun,iostat=ios,iomsg=message) if(ios.ne.0)then - write(stderr,'(*(a:,1x))')& - & '<ERROR> *filewrite*:',filename,trim(message) - error stop 4 + write(stderr,'(*(a:,1x))')'<ERROR> *filewrite*:',trim(message) + stop 2 endif - enddo - ! close file - close(unit=lun,iostat=ios,iomsg=message) - if(ios.ne.0)then - write(stderr,'(*(a:,1x))')'<ERROR> *filewrite*:',trim(message) - error stop 2 endif +end subroutine fileclose + +subroutine filewrite(filename,filedata) +! 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 +character(len=256) :: message + call fileopen(filename,lun) + if(lun.ne.-1)then ! program currently stops on error on open, but might + ! want it to continue so -1 (unallowed LUN) indicates error + ! 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))')& + & '<ERROR> *filewrite*:',filename,trim(message) + stop 4 + endif + enddo + endif + ! close file + call fileclose(lun) + end subroutine filewrite +subroutine validate_toml_data(input) +!> verify a string array is a valid fpm.toml file +! +use tomlf, only : toml_parse +use fpm_toml, only : toml_table, toml_serializer +implicit none +character(kind=tfc,len=:),intent(in),allocatable :: input(:) +character(len=1), parameter :: nl = new_line('a') +type(toml_table), allocatable :: table +character(kind=tfc, len=:), allocatable :: joined_string +type(toml_serializer) :: ser + +! you have to add a newline character by using the intrinsic +! function `new_line("a")` to get the lines processed correctly. +joined_string = join(input,right=nl) + +if (allocated(table)) deallocate(table) +call toml_parse(table, joined_string) +if (allocated(table)) then + if(settings%verbose)then + ! If the TOML file is successfully parsed the table will be allocated and + ! can be written to the standard output by passing the `toml_serializer` + ! as visitor to the table. + call table%accept(ser) + endif + call table%destroy +endif + +end subroutine validate_toml_data + +pure function join(str,sep,trm,left,right) result (string) + +!> M_strings::join(3f): append an array of character variables with specified separator into a single CHARACTER variable +!> +!>##NAME +!> join(3f) - [M_strings:EDITING] append CHARACTER variable array into +!> a single CHARACTER variable with specified separator +!> (LICENSE:PD) +!> +!>##SYNOPSIS +!> +!> pure function join(str,sep,trm,left,right) result (string) +!> +!> character(len=*),intent(in) :: str(:) +!> character(len=*),intent(in),optional :: sep +!> logical,intent(in),optional :: trm +!> character(len=*),intent(in),optional :: right +!> character(len=*),intent(in),optional :: left +!> character(len=:),allocatable :: string +!> +!>##DESCRIPTION +!> JOIN(3f) appends the elements of a CHARACTER array into a single +!> CHARACTER variable, with elements 1 to N joined from left to right. +!> By default each element is trimmed of trailing spaces and the +!> default separator is a null string. +!> +!>##OPTIONS +!> STR(:) array of CHARACTER variables to be joined +!> SEP separator string to place between each variable. defaults +!> to a null string. +!> LEFT string to place at left of each element +!> RIGHT string to place at right of each element +!> TRM option to trim each element of STR of trailing +!> spaces. Defaults to .TRUE. +!> +!>##RESULT +!> STRING CHARACTER variable composed of all of the elements of STR() +!> appended together with the optional separator SEP placed +!> between the elements. +!> +!>##EXAMPLE +!> +!> Sample program: +!> +!> program demo_join +!> use M_strings, only: join +!> implicit none +!> character(len=:),allocatable :: s(:) +!> character(len=:),allocatable :: out +!> integer :: i +!> s=[character(len=10) :: 'United',' we',' stand,', & +!> & ' divided',' we fall.'] +!> out=join(s) +!> write(*,'(a)') out +!> write(*,'(a)') join(s,trm=.false.) +!> write(*,'(a)') (join(s,trm=.false.,sep='|'),i=1,3) +!> write(*,'(a)') join(s,sep='<>') +!> write(*,'(a)') join(s,sep=';',left='[',right=']') +!> write(*,'(a)') join(s,left='[',right=']') +!> write(*,'(a)') join(s,left='>>') +!> end program demo_join +!> +!> Expected output: +!> +!> United we stand, divided we fall. +!> United we stand, divided we fall. +!> United | we | stand, | divided | we fall. | +!> United | we | stand, | divided | we fall. | +!> United | we | stand, | divided | we fall. | +!> United<> we<> stand,<> divided<> we fall.<> +!> [United];[ we];[ stand,];[ divided];[ we fall.]; +!> [United][ we][ stand,][ divided][ we fall.] +!> >>United>> we>> stand,>> divided>> we fall. +!> +!>##AUTHOR +!> John S. Urban +!> +!>##LICENSE +!> Public Domain + +character(len=*,kind=tfc),intent(in) :: str(:) +character(len=*),intent(in),optional :: sep, right, left +logical,intent(in),optional :: trm +character(len=:,kind=tfc),allocatable :: string +integer :: i +logical :: trm_local +character(len=:),allocatable :: sep_local, left_local, right_local + + if(present(sep))then ; sep_local=sep ; else ; sep_local='' ; endif + if(present(trm))then ; trm_local=trm ; else ; trm_local=.true. ; endif + if(present(left))then ; left_local=left ; else ; left_local='' ; endif + if(present(right))then ; right_local=right ; else ; right_local='' ; endif + + string='' + do i = 1,size(str) + if(trm_local)then + string=string//left_local//trim(str(i))//right_local//sep_local + else + string=string//left_local//str(i)//right_local//sep_local + endif + enddo +end function join + end subroutine cmd_new end module fpm_cmd_new diff --git a/fpm/src/fpm_command_line.f90 b/fpm/src/fpm_command_line.f90 index 85bfc94..5bfcc8d 100644 --- a/fpm/src/fpm_command_line.f90 +++ b/fpm/src/fpm_command_line.f90 @@ -55,6 +55,8 @@ type, extends(fpm_cmd_settings) :: fpm_new_settings logical :: with_test=.false. logical :: with_lib=.true. logical :: with_example=.false. + logical :: with_full=.false. + logical :: with_bare=.false. logical :: backfill=.true. end type @@ -205,21 +207,23 @@ contains & --app F & & --test F & & --example F & - & --backfill F& - & --verbose F',& + & --backfill F & + & --full F & + & --bare F & + & --verbose:V F',& & help_new, version_text) select case(size(unnamed)) case(1) write(stderr,'(*(g0,/))')'<ERROR> directory name required' write(stderr,'(*(7x,g0,/))') & - & '<USAGE> fpm new NAME [--lib|--src] [--app] [--test] [--example] [--backfill]' + & '<USAGE> fpm new NAME [[--lib|--src] [--app] [--test] [--example]]|[--full|--bare] [--backfill]' stop 1 case(2) name=trim(unnamed(2)) case default write(stderr,'(g0)')'<ERROR> only one directory name allowed' write(stderr,'(7x,g0)') & - & '<USAGE> fpm new NAME [--lib|--src] [--app] [--test] [--example] [--backfill]' + & '<USAGE> fpm new NAME [[--lib|--src] [--app] [--test] [--example]]| [--full|--bare] [--backfill]' stop 2 end select !*! canon_path is not converting ".", etc. @@ -235,8 +239,19 @@ contains endif allocate(fpm_new_settings :: cmd_settings) - - if (any( specified([character(len=10) :: 'src','lib','app','test','example']) ) )then + if (any( specified([character(len=10) :: 'src','lib','app','test','example','bare'])) & + & .and.lget('full') )then + write(stderr,'(*(a))')& + &'<ERROR> --full and any of [--src|--lib,--app,--test,--example,--bare]', & + &' are mutually exclusive.' + stop 5 + elseif (any( specified([character(len=10) :: 'src','lib','app','test','example','full'])) & + & .and.lget('bare') )then + write(stderr,'(*(a))')& + &'<ERROR> --bare and any of [--src|--lib,--app,--test,--example,--full]', & + &' are mutually exclusive.' + stop 3 + elseif (any( specified([character(len=10) :: 'src','lib','app','test','example']) ) )then cmd_settings=fpm_new_settings(& & backfill=lget('backfill'), & & name=name, & @@ -252,7 +267,10 @@ contains & with_executable=.true., & & with_lib=.true., & & with_test=.true., & - & with_example=lget('verbose'), & + & with_example=.true., & + !*!& with_example=lget('full'), & + & with_full=lget('full'), & + & with_bare=lget('bare'), & & verbose=lget('verbose') ) endif @@ -480,7 +498,8 @@ contains ' ', & ' build [--compiler COMPILER_NAME] [--release] [--list] ', & ' help [NAME(s)] ', & - ' new NAME [--lib|--src] [--app] [--test] [--example] [--backfill] ', & + ' new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & + ' [--full|--bare][--backfill] ', & ' update [NAME(s)] [--fetch-only] [--clean] [--verbose] ', & ' list [--list] ', & ' run [[--target] NAME(s)] [--release] [--runner "CMD"] [--list] [--example] ', & @@ -593,7 +612,8 @@ contains ' Their syntax is ', & ' ', & ' build [--release] [--list] [--compiler COMPILER_NAME] ', & - ' new NAME [--lib|--src] [--app] [--test] [--example] [--backfill] ', & + ' new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & + ' [--full|--bare][--backfill] ', & ' update [NAME(s)] [--fetch-only] [--clean] ', & ' run [[--target] NAME(s)] [--release] [--list] [--example] ', & ' [--runner "CMD"] [--compiler COMPILER_NAME] [-- ARGS] ', & @@ -795,8 +815,8 @@ contains 'NAME ', & ' new(1) - the fpm(1) subcommand to initialize a new project ', & 'SYNOPSIS ', & - ' fpm new NAME [--lib|--src] [--app] [--test] [--example] [--backfill] ', & - ' ', & + ' fpm new NAME [[--lib|--src] [--app] [--test] [--example]]| ', & + ' [--full|--bare][--backfill] ', & ' fpm new --help|--version ', & ' ', & 'DESCRIPTION ', & @@ -864,13 +884,17 @@ contains ' "fpm new myname --backfill" will create the missing ', & ' app/ and test/ directories and programs. ', & ' ', & - ' --verbose By default a minimal manifest file ("fpm.toml") is ', & + ' --full By default a minimal manifest file ("fpm.toml") is ', & ' created that depends on auto-discovery and (as noted) ', & ' the example/ directory is only created if explicitly ', & ' asked for. With this option a much more extensive ', & ' manifest sample is written and the example/ directory ', & ' is created by default as well. ', & ' ', & + ' --bare A minimal manifest file ("fpm.toml") is created and ', & + ' a ".gitignore" and "README.md" file is created but no ', & + ' directories or sample Fortran is generated. ', & + ' ', & ' --help print this help and exit ', & ' --version print program version information and exit ', & ' ', & |