aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn S. Urban <urbanjost@comcast.net>2020-12-22 00:37:10 -0500
committerJohn S. Urban <urbanjost@comcast.net>2020-12-22 00:37:10 -0500
commit0fe6b1fe311ad43acb2ecd98fb2b98cf40636c95 (patch)
treec8eb1631574b47a6dffa413542a4a7948391acdc
parentd435b766f2b3193eb077db0d2e8025daa29d55ef (diff)
downloadfpm-0fe6b1fe311ad43acb2ecd98fb2b98cf40636c95.tar.gz
fpm-0fe6b1fe311ad43acb2ecd98fb2b98cf40636c95.zip
complicating new
-rw-r--r--fpm/src/fpm/cmd/new.f90295
-rw-r--r--fpm/src/fpm_command_line.f9048
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 ', &
' ', &