TOP DIRECTORY NAME PROCESSING see if requested new directory already exists and process appropriately temporarily change to new directory as a test. NB: System dependent
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
type(fpm_new_settings), | intent(in) | :: | settings |
subroutine cmd_new(settings) type(fpm_new_settings), intent(in) :: settings 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(:) !> TOP DIRECTORY NAME PROCESSING !> see if requested new directory already exists and process appropriately 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 !> temporarily change to new directory as a test. NB: System dependent call run('cd '//settings%name) ! NOTE: need some system routines to handle filenames like "." ! like realpath() or getcwd(). bname=basename(settings%name) littlefile=[character(len=80) :: '# '//bname, 'My cool new project!'] ! create NAME/README.md call warnwrite(join_path(settings%name, 'README.md'), littlefile) ! start building NAME/fpm.toml if(settings%with_full)then tomlfile=[character(len=80) :: & &' # This is your fpm(Fortran Package Manager) manifest file ',& &' # ("fpm.toml"). It is heavily annotated to help guide you though ',& &' # customizing a package build, although the defaults are sufficient ',& &' # for many basic packages. ',& &' # ',& &' # The manifest file is not only used to provide metadata identifying ',& &' # your project (so it can be used by others as a dependency). It can ',& &' # specify where your library and program sources live, what the name ',& &' # of the executable(s) will be, what files to build, dependencies on ',& &' # other fpm packages, and what external libraries are required. ',& &' # ',& &' # The manifest format must conform to the TOML configuration file ',& &' # standard. ',& &' # ',& &' # TOML files support flexible use of white-space and commenting of the ',& &' # configuration data, but for clarity in this sample active directives ',& &' # begin in column one. Inactive example directives are commented ',& &' # out with a pound character ("#") but begin in column one as well. ',& &' # Commentary begins with a pound character in column three. ',& &' # ',& &' # This file draws heavily upon the following references: ',& &' # ',& &' # The fpm home page at ',& &' # https://github.com/fortran-lang/fpm ',& &' # A complete list of keys and their attributes at ',& &' # https://github.com/fortran-lang/fpm/blob/main/manifest-reference.md ',& &' # examples of fpm project packaging at ',& &' # https://github.com/fortran-lang/fpm/blob/main/PACKAGING.md ',& &' # The Fortran TOML file interface and it''s references at ',& &' # https://github.com/toml-f/toml-f ',& &' # ',& &' #----------------------- ',& &' # project Identification ',& &' #----------------------- ',& &' # We begin with project metadata at the manifest root. This data is designed ',& &' # to aid others when searching for the project in a repository and to ',& &' # identify how and when to contact the package supporters. ',& &' ',& &'name = "'//bname//'"',& &' # The project name (required) is how the project will be referred to. ',& &' # The name is used by other packages using it as a dependency. It also ',& &' # is used as the default name of any library built and the optional ',& &' # default executable built from app/main.f90. It must conform to the rules ',& &' # for a Fortran variable name. ',& &' ',& &'version = "0.1.0" ',& &' # The project version number is a string. A recommended scheme for ',& &' # specifying versions is the Semantic Versioning scheme. ',& &' ',& &'license = "license" ',& &' # Licensing information specified using SPDX identifiers is preferred ',& &' # (eg. "Apache-2.0 OR MIT" or "LGPL-3.0-or-later"). ',& &' ',& &'maintainer = "jane.doe@example.com" ',& &' # Information on the project maintainer and means to reach out to them. ',& &' ',& &'author = "Jane Doe" ',& &' # Information on the project author. ',& &' ',& &'copyright = "Copyright 2020 Jane Doe" ',& &' # A statement clarifying the Copyright status of the project. ',& &' ',& &'#description = "A short project summary in plain text" ',& &' # The description provides a short summary on the project. It should be ',& &' # plain text and not use any markup formatting. ',& &' ',& &'#categories = ["fortran", "graphics"] ',& &' # Categories associated with the project. Listing only one is preferred. ',& &' ',& &'#keywords = ["hdf5", "mpi"] ',& &' # The keywords field is an array of strings describing the project. ',& &' ',& &'#homepage = "https://stdlib.fortran-lang.org" ',& &' # URL to the webpage of the project. ',& &' ',& &' # ----------------------------------------- ',& &' # We are done with identifying the project. ',& &' # ----------------------------------------- ',& &' # ',& &' # Now lets start describing how the project should be built. ',& &' # ',& &' # Note tables would go here but we will not be talking about them (much)!!' ,& &' # ',& &' # Tables are a way to explicitly specify large numbers of programs in ',& &' # a compact format instead of individual per-program entries in the ',& &' # [[executable]], [[test]], and [[example]] sections to follow but ',& &' # will not be discussed further except for the following notes: ',& &' # ',& &' # + Tables must appear (here) before any sections are declared. Once a ',& &' # section is specified in a TOML file everything afterwards must be ',& &' # values for that section or the beginning of a new section. A simple ',& &' # example looks like: ',& &' ',& &'#executable = [ ',& &'# { name = "a-prog" }, ',& &'# { name = "app-tool", source-dir = "tool" }, ',& &'# { name = "fpm-man", source-dir = "tool", main="fman.f90" } ',& &'#] ',& &' ',& &' # This would be in lieue of the [[executable]] section found later in this ',& &' # configuration file. ',& &' # + See the reference documents (at the beginning of this document) ',& &' # for more information on tables if you have long lists of programs ',& &' # to build and are not simply depending on auto-detection. ',& &' # ',& &' # Now lets begin the TOML sections (lines beginning with "[") ... ',& &' # ',& &' ',& &'[install] # Options for the "install" subcommand ',& &' ',& &' # When you run the "install" subcommand only executables are installed by ',& &' # default on the local system. Library projects that will be used outside of ',& &' # "fpm" can set the "library" boolean to also allow installing the module ',& &' # files and library archive. Without this being set to "true" an "install" ',& &' # subcommand ignores parameters that specify library installation. ',& &' ',& &'library = false ',& &' ',& &'[build] # General Build Options ',& &' ',& &' ### Automatic target discovery ',& &' # ',& &' # Normally fpm recursively searches the app/, example/, and test/ directories ',& &' # for program sources and builds them. To disable this automatic discovery of ',& &' # program targets set the following to "false": ',& &' ',& &'#auto-executables = true ',& &'#auto-examples = true ',& &'#auto-tests = true ',& &' ',& &' ### Package-level External Library Links ',& &' # ',& &' # To declare link-time dependencies on external libraries a list of ',& &' # native libraries can be specified with the "link" entry. You may ',& &' # have one library name or a list of strings in case several ',& &' # libraries should be linked. This list of library dependencies is ',& &' # exported to dependent packages. You may have to alter your library ',& &' # search-path to ensure the libraries can be accessed. Typically, ',& &' # this is done with the LD_LIBRARY_PATH environment variable on ULS ',& &' # (Unix-Like Systems). You only specify the core name of the library ',& &' # (as is typical with most programming environments, where you ',& &' # would specify "-lz" on your load command to link against the zlib ',& &' # compression library even though the library file would typically be ',& &' # a file called "libz.a" "or libz.so"). So to link against that library ',& &' # you would specify: ',& &' ',& &'#link = "z" ',& &' ',& &' # Note that in some cases the order of the libraries matters: ',& &' ',& &'#link = ["blas", "lapack"] ',& &''] endif 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%with_full)then tomlfile=[character(len=80) :: tomlfile, & &'[library] ',& &' ',& &' # You can change the name of the directory to search for your library ',& &' # source from the default of "src/". Library targets are exported ',& &' # and usable by other projects. ',& &' ',& &'source-dir="src" ',& &' ',& &' # this can be a list: ',& &' ',& &'#source-dir=["src", "src2"] ',& &' ',& &' # More complex libraries may organize their modules in subdirectories. ',& &' # For modules in a top-level directory fpm requires (but does not ',& &' # enforce) that: ',& &' # ',& &' # + The module has the same name as the source file. This is important. ',& &' # + There should be only one module per file. ',& &' # ',& &' # These two requirements simplify the build process for fpm. As Fortran ',& &' # compilers emit module files (.mod) with the same name as the module ',& &' # itself (but not the source file, .f90), naming the module the same ',& &' # as the source file allows fpm to: ',& &' # ',& &' # + Uniquely and exactly map a source file (.f90) to its object (.o) ',& &' # and module (.mod) files. ',& &' # + Avoid conflicts with modules of the same name that could appear ',& &' # in dependency packages. ',& &' # ',& &' ### Multi-level library source ',& &' # You can place your module source files in any number of levels of ',& &' # subdirectories inside your source directory, but there are certain naming ',& &' # conventions to be followed -- module names must contain the path components ',& &' # of the directory that its source file is in. ',& &' # ',& &' # This rule applies generally to any number of nested directories and ',& &' # modules. For example, src/a/b/c/d.f90 must define a module called a_b_c_d. ',& &' # Again, this is not enforced but may be required in future releases. ',& &''] endif ! create placeholder module src/bname.f90 littlefile=[character(len=80) :: & &'module '//to_fortran_name(bname), & &' implicit none', & &' private', & &'', & &' public :: say_hello', & &'contains', & &' subroutine say_hello', & &' print *, "Hello, '//bname//'!"', & &' end subroutine say_hello', & &'end module '//to_fortran_name(bname)] ! create NAME/src/NAME.f90 call warnwrite(join_path(settings%name, 'src', bname//'.f90'),& & littlefile) endif if(settings%with_full)then tomlfile=[character(len=80) :: tomlfile ,& &'[dependencies] ',& &' ',& &' # Inevitably, you will want to be able to include other packages in ',& &' # a project. Fpm makes this incredibly simple, by taking care of ',& &' # fetching and compiling your dependencies for you. You just tell it ',& &' # what your dependencies names are, and where to find them. ',& &' # ',& &' # If you are going to distribute your package only place dependencies ',& &' # here someone using your package as a remote dependency needs built. ',& &' # You can define dependencies just for developer executables in the ',& &' # next section, or even for specific executables as we will see below ',& &' # (Then fpm will still fetch and compile it when building your ',& &' # developer executables, but users of your library will not have to). ',& &' # ',& &' ## GLOBAL DEPENDENCIES (exported with your project) ',& &' # ',& &' # Typically, dependencies are defined by specifying the project''s ',& &' # git repository. ',& &' # ',& &' # You can be specific about which version of a dependency you would ',& &' # like. By default the latest default branch is used. You can ',& &' # optionally specify a branch, a tag or a commit value. ',& &' # ',& &' # So here are several alternates for specifying a remote dependency (you ',& &' # can have at most one of "branch", "rev" or "tag" present): ',& &' ',& &'#stdlib = { git = "https://github.com/LKedward/stdlib-fpm.git" } ',& &'#stdlib = {git="https://github.com/LKedward/stdlib-fpm.git",branch = "master" },',& &'#stdlib = {git="https://github.com/LKedward/stdlib-fpm.git", tag = "v0.1.0" }, ',& &'#stdlib = {git="https://github.com/LKedward/stdlib-fpm.git", rev = "5a9b7a8" }. ',& &' ',& &' # There may be multiple packages listed: ',& &' ',& &'#M_strings = { git = "https://github.com/urbanjost/M_strings.git" } ',& &'#M_time = { git = "https://github.com/urbanjost/M_time.git" } ',& &' ',& &' # ',& &' # You can even specify the local path to another project if it is in ',& &' # a sub-folder (If for example you have got another fpm package **in ',& &' # the same repository**) like this: ',& &' ',& &'#M_strings = { path = "M_strings" } ',& &' ',& &' # This tells fpm that we depend on a crate called M_strings which is found ',& &' # in the M_strings folder (relative to the fpm.toml it’s written in). ',& &' # ',& &' # For a more verbose layout use normal tables rather than inline tables ',& &' # to specify dependencies: ',& &' ',& &'#[dependencies.toml-f] ',& &'#git = "https://github.com/toml-f/toml-f" ',& &'#rev = "2f5eaba864ff630ba0c3791126a3f811b6e437f3" ',& &' ',& &' # Now you can use any modules from these libraries anywhere in your ',& &' # code -- whether is in your library source or a program source. ',& &' ',& &'[dev-dependencies] ',& &' ',& &' ## Dependencies Only for Development ',& &' # ',& &' # You can specify dependencies your library or application does not ',& &' # depend on in a similar way. The difference is that these will not ',& &' # be exported as part of your project to those using it as a remote ',& &' # dependency. ',& &' # ',& &' # Currently, like a global dependency it will still be available for ',& &' # all codes. It is up to the developer to ensure that nothing except ',& &' # developer test programs rely upon it. ',& &' ',& &'#M_msg = { git = "https://github.com/urbanjost/M_msg.git" } ',& &'#M_verify = { git = "https://github.com/urbanjost/M_verify.git" } ',& &''] endif 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%with_full)then tomlfile=[character(len=80) :: tomlfile, & &' #----------------------------------- ',& &' ## Application-specific declarations ',& &' #----------------------------------- ',& &' # Now lets begin entries for the TOML tables (lines beginning with "[[") ',& &' # that describe the program sources -- applications, tests, and examples. ',& &' # ',& &' # First we will configuration individual applications run with "fpm run". ',& &' # ',& &' # + the "name" entry for the executable to be built must always ',& &' # be specified. The name must satisfy the rules for a Fortran ',& &' # variable name. This will be the name of the binary installed by ',& &' # the "install" subcommand and used on the "run" subcommand. ',& &' # + The source directory for each executable can be adjusted by the ',& &' # "source-dir" entry. ',& &' # + The basename of the source file containing the program body can ',& &' # be specified with the "main" entry. ',& &' # + Executables can also specify their own external package and ',& &' # library link dependencies. ',& &' # ',& &' # Currently, like a global dependency any external package dependency ',& &' # will be available for all codes. It is up to the developer to ensure ',& &' # that nothing except the application programs specified rely upon it. ',& &' # ',& &' # Note if your application needs to use a module internally, but you do not ',& &' # intend to build it as a library to be used in other projects, you can ',& &' # include the module in your program source file or directory as well. ',& &' ',& &'[[executable]] ',& &'name="'//bname//'"',& &'source-dir="app" ',& &'main="main.f90" ',& &' ',& &' # You may repeat this pattern to define additional applications. For instance,',& &' # the following sample illustrates all accepted options, where "link" and ',& &' # "executable.dependencies" keys are the same as the global external library ',& &' # links and package dependencies described previously except they apply ',& &' # only to this executable: ',& &' ',& &'#[[ executable ]] ',& &'#name = "app-name" ',& &'#source-dir = "prog" ',& &'#main = "program.f90" ',& &'#link = "z" ',& &'#[executable.dependencies] ',& &'#M_CLI = { git = "https://github.com/urbanjost/M_CLI.git" } ',& &'#helloff = { git = "https://gitlab.com/everythingfunctional/helloff.git" } ',& &'#M_path = { git = "https://github.com/urbanjost/M_path.git" } ',& &''] endif if(exists(bname//'/src/'))then littlefile=[character(len=80) :: & &'program main', & &' use '//to_fortran_name(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 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%with_full)then tomlfile=[character(len=80) :: tomlfile ,& &'[[test]] ',& &' ',& &' # The same declarations can be made for test programs, which are ',& &' # executed with the "fpm test" command and are not build when your ',& &' # package is used as a dependency by other packages. These are ',& &' # typically unit tests of the package only used during package ',& &' # development. ',& &' ',& &'name="runTests" ',& &'source-dir="test" ',& &'main="check.f90" ',& &' ',& &' # you may repeat this pattern to add additional explicit test program ',& &' # parameters. The following example contains a sample of all accepted ',& &' # options. ',& &' ',& &'#[[ test ]] ',& &'#name = "tester" ',& &'#source-dir="test" ',& &'#main="tester.f90" ',& &'#link = ["blas", "lapack"] ',& &'#[test.dependencies] ',& &'#M_CLI2 = { git = "https://github.com/urbanjost/M_CLI2.git" } ',& &'#M_io = { git = "https://github.com/urbanjost/M_io.git" } ',& &'#M_system= { git = "https://github.com/urbanjost/M_system.git" } ',& &''] endif littlefile=[character(len=80) :: & &'program check', & &'implicit none', & &'', & &'print *, "Put some tests in here!"', & &'end program check'] ! create NAME/test/check.f90 call warnwrite(join_path(settings%name, 'test/check.f90'), littlefile) endif 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%with_full)then tomlfile=[character(len=80) :: tomlfile, & &'[[example]] ',& &' ',& &' # Example applications for a project are defined here. ',& &' # These are run via "fpm run --example NAME" and like the ',& &' # test applications, are not built when this package is used as a ',& &' # dependency by other packages. ',& &' ',& &'name="demo" ',& &'source-dir="example" ',& &'main="demo.f90" ',& &' ',& &' # ',& &' # you may add additional programs to the example table. The following ',& &' # example contains a sample of all accepted options ',& &' ',& &'#[[ example ]] ',& &'#name = "example-tool" ',& &'#source-dir="example" ',& &'#main="tool.f90" ',& &'#link = "z" ',& &'#[example.dependencies] ',& &'#M_kracken95 = { git = "https://github.com/urbanjost/M_kracken95.git" } ',& &'#datetime = {git = "https://github.com/wavebitscientific/datetime-fortran.git" }',& &''] endif littlefile=[character(len=80) :: & &'program demo', & &'implicit none', & &'', & &'print *, "Put some examples in here!"', & &'end program demo'] ! create NAME/example/demo.f90 call warnwrite(join_path(settings%name, 'example/demo.f90'), littlefile) endif ! now that built it write NAME/fpm.toml 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 if(which('git')/='')then call run('git init ' // settings%name) endif contains function git_metadata(what) result(returned) !> get metadata values such as email address and git name from git(1) or return appropriate default use fpm_filesystem, only : get_temp_filename, getline character(len=*), intent(in) :: what ! keyword designating what git metatdata to query character(len=:), allocatable :: returned ! value to return for requested keyword character(len=:), allocatable :: command character(len=:), allocatable :: temp_filename character(len=:), allocatable :: iomsg character(len=:), allocatable :: temp_value integer :: stat, unit temp_filename = get_temp_filename() ! for known keywords set default value for RETURNED and associated git(1) command for query select case(what) case('uname') returned = "Jane Doe" command = "git config --get user.name > " // temp_filename case('email') returned = "jane.doe@example.com" command = "git config --get user.email > " // temp_filename case default write(stderr,'(*(g0,1x))')& & '<ERROR> *git_metadata* unknown metadata name ',trim(what) returned='' return end select ! Execute command if git(1) is in command path if(which('git')/='')then call run(command, exitstat=stat) if (stat /= 0) then ! If command failed just return default return else ! Command did not return an error so try to read expected output file open(file=temp_filename, newunit=unit,iostat=stat) if(stat == 0)then ! Read file into a scratch variable until status of doing so is checked call getline(unit, temp_value, stat, iomsg) if (stat == 0 .and. temp_value /= '') then ! Return output from successful command returned=temp_value endif endif ! Always do the CLOSE because a failed open has unpredictable results. ! Add IOSTAT so a failed close does not cause program to stop close(unit, status="delete",iostat=stat) endif endif end function git_metadata subroutine create_verified_basic_manifest(filename) !> create a basic but verified default manifest file use fpm_toml, only : toml_table, toml_serialize, 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(package_config_t) :: package type(error_t), allocatable :: error integer :: lun character(len=8) :: date character(:), allocatable :: output if(exists(filename))then write(stderr,'(*(g0,1x))')'<INFO> ',filename,& & 'already exists. Not overwriting' return endif !> get date to put into metadata in manifest file "fpm.toml" call date_and_time(DATE=date) table = toml_table() 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", git_metadata('uname')) call set_value(table, "maintainer", git_metadata('email')) call set_value(table, "copyright", 'Copyright '//date(1:4)//', '//git_metadata('uname')) ! continue building of manifest ! ... call new_package(package, table, error=error) if (allocated(error)) call fpm_stop( 3,'') output = toml_serialize(table) if(settings%verbose)then print '(a)', output endif write(lun, '(a)') output call fileclose(lun) ! fileopen stops on error end subroutine create_verified_basic_manifest subroutine validate_toml_data(input) !> verify a string array is a valid fpm.toml file ! use tomlf, only : toml_load use fpm_toml, only : toml_table, toml_serialize 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 ! 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_load(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 by `toml_serialize` to the standard output print '(a)', toml_serialize(table) endif call table%destroy endif end subroutine validate_toml_data end subroutine cmd_new