diff options
-rw-r--r-- | bootstrap/src/Build.hs | 8 | ||||
-rw-r--r-- | bootstrap/src/Fpm.hs | 198 | ||||
-rw-r--r-- | fpm/src/fpm.f90 | 2 | ||||
-rw-r--r-- | fpm/src/fpm_compiler.f90 | 2 | ||||
-rw-r--r-- | fpm/src/fpm_source_parsing.f90 | 35 | ||||
-rw-r--r-- | fpm/src/fpm_sources.f90 | 13 | ||||
-rw-r--r-- | fpm/src/fpm_strings.f90 | 25 | ||||
-rw-r--r-- | fpm/test/fpm_test/test_source_parsing.f90 | 61 | ||||
-rw-r--r-- | manifest-reference.md | 6 |
9 files changed, 324 insertions, 26 deletions
diff --git a/bootstrap/src/Build.hs b/bootstrap/src/Build.hs index 32d1979..724a1c6 100644 --- a/bootstrap/src/Build.hs +++ b/bootstrap/src/Build.hs @@ -75,7 +75,7 @@ buildProgram programDirectory' libraryDirectories sourceExtensions buildDirector libraryModules <- findAvailableModules libraryDirectories let programDirectory = foldl1 (</>) (splitDirectories programDirectory') let buildDirectory = foldl1 (</>) (splitDirectories buildDirectory') - let includeFlags = map (includeFlag ++) libraryDirectories + let includeFlags = (includeFlag ++ buildDirectory) : map (includeFlag ++) libraryDirectories sourceFiles <- getDirectoriesFiles [programDirectory] sourceExtensions rawSources <- mapM sourceFileToRawSource sourceFiles let sources' = map processRawSource rawSources @@ -108,7 +108,7 @@ buildProgram programDirectory' libraryDirectories sourceExtensions buildDirector in fileMatcher &?> \(objectFile : _) -> do need (sourceFile : directDependencies) cmd compiler - ["-c", moduleFlag ++ buildDirectory] + ["-c", moduleFlag, buildDirectory] includeFlags flags ["-o", objectFile, sourceFile] @@ -130,7 +130,7 @@ buildLibrary buildLibrary libraryDirectory sourceExtensions buildDirectory (CompilerSettings { compilerSettingsCompiler = compiler, compilerSettingsFlags = flags, compilerSettingsModuleFlag = moduleFlag, compilerSettingsIncludeFlag = includeFlag }) libraryName otherLibraryDirectories = do otherModules <- findAvailableModules otherLibraryDirectories - let includeFlags = map (includeFlag ++) otherLibraryDirectories + let includeFlags = (includeFlag ++ buildDirectory) : map (includeFlag ++) otherLibraryDirectories sourceFiles <- getDirectoriesFiles [libraryDirectory] sourceExtensions rawSources <- mapM sourceFileToRawSource sourceFiles let sources = map processRawSource rawSources @@ -160,7 +160,7 @@ buildLibrary libraryDirectory sourceExtensions buildDirectory (CompilerSettings in fileMatcher &?> \(objectFile : _) -> do need (sourceFile : directDependencies) cmd compiler - ["-c", moduleFlag ++ buildDirectory] + ["-c", moduleFlag, buildDirectory] includeFlags flags ["-o", objectFile, sourceFile] diff --git a/bootstrap/src/Fpm.hs b/bootstrap/src/Fpm.hs index bec7706..10e14fe 100644 --- a/bootstrap/src/Fpm.hs +++ b/bootstrap/src/Fpm.hs @@ -71,6 +71,7 @@ import System.Exit ( ExitCode(..) , exitWith ) import System.Process ( readProcess + , readProcessWithExitCode , system ) import Toml ( TomlCodec @@ -733,6 +734,185 @@ defineCompilerSettings specifiedFlags compiler release , compilerSettingsModuleFlag = "-J" , compilerSettingsIncludeFlag = "-I" } + | "f95" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [ "-O3" + , "-Wimplicit-interface" + , "-fPIC" + , "-fmax-errors=1" + , "-ffast-math" + , "-funroll-loops" + ] + else + [ "-Wall" + , "-Wextra" + , "-Wimplicit-interface" + , "-fPIC" + , "-fmax-errors=1" + , "-g" + , "-fbounds-check" + , "-fcheck-array-temporaries" + , "-Wno-maybe-uninitialized" + , "-Wno-uninitialized" + , "-fbacktrace" + ] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-J" + , compilerSettingsIncludeFlag = "-I" + } + | "nvfortran" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [ "-Mbackslash" + ] + else + [ "-Minform=inform" + , "-Mbackslash" + , "-g" + , "-Mbounds" + , "-Mchkptr" + , "-Mchkstk" + , "-traceback" + ] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-module" + , compilerSettingsIncludeFlag = "-I" + } + | "ifort" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [ "-fp-model", "precise" + , "-pc", "64" + , "-align", "all" + , "-error-limit", "1" + , "-reentrancy", "threaded" + , "-nogen-interfaces" + , "-assume", "byterecl" + , "-assume", "nounderscore" + ] + else + [ "-warn", "all" + , "-check:all:noarg_temp_created" + , "-error-limit", "1" + , "-O0" + , "-g" + , "-assume", "byterecl" + , "-traceback" + ] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-module" + , compilerSettingsIncludeFlag = "-I" + } + | "ifx" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [] + else + [] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-module" + , compilerSettingsIncludeFlag = "-I" + } + | "pgfortran" `isInfixOf` compiler || "pgf90" `isInfixOf` compiler || "pgf95" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [] + else + [] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-module" + , compilerSettingsIncludeFlag = "-I" + } + | "flang" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [] + else + [] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-module" + , compilerSettingsIncludeFlag = "-I" + } + | "lfc" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [] + else + [] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-M" + , compilerSettingsIncludeFlag = "-I" + } + | "nagfor" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [ "-O4" + , "-coarray=single" + , "-PIC" + ] + else + [ "-g" + , "-C=all" + , "-O0" + , "-gline" + , "-coarray=single" + , "-PIC" + ] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-mdir" + , compilerSettingsIncludeFlag = "-I" + } + | "crayftn" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [] + else + [] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-J" + , compilerSettingsIncludeFlag = "-I" + } + | "xlf90" `isInfixOf` compiler + = let flags = case specifiedFlags of + [] -> if release + then + [] + else + [] + fs -> fs + in return $ CompilerSettings { compilerSettingsCompiler = compiler + , compilerSettingsFlags = flags + , compilerSettingsModuleFlag = "-qmoddir" + , compilerSettingsIncludeFlag = "-I" + } | otherwise = do putStrLn $ "Sorry, compiler is currently unsupported: " ++ compiler @@ -808,7 +988,23 @@ makeBuildPrefix :: FilePath -> [String] -> IO FilePath makeBuildPrefix compiler flags = do -- TODO Figure out what other info should be part of this -- Probably version, and make sure to not include path to the compiler - versionInfo <- readProcess compiler ["--version"] [] + versionInfo <- do + (exitCode, stdout, stderr) <- readProcessWithExitCode compiler + ["--version"] + [] + case exitCode of + ExitSuccess -> case stdout of + "" -> return stderr -- Guess this compiler outputs version info to stderr instead? + _ -> return stdout + _ -> do -- guess this compiler doesn't support the --version option. let's try -version + (exitCode, stdout, stderr) <- readProcessWithExitCode compiler + ["-version"] + [] + case exitCode of + ExitSuccess -> case stdout of + "" -> return stderr -- Guess this compiler outputs version info to stderr instead? + _ -> return stdout + _ -> return "" -- Don't know how to get version info, we'll let defineCompilerSettings report it as unsupported let compilerName = last (splitDirectories compiler) let versionHash = abs (hash versionInfo) let flagsHash = abs (hash flags) diff --git a/fpm/src/fpm.f90 b/fpm/src/fpm.f90 index 79ced14..d91b1d4 100644 --- a/fpm/src/fpm.f90 +++ b/fpm/src/fpm.f90 @@ -1,5 +1,5 @@ module fpm -use fpm_strings, only: string_t, str_ends_with, operator(.in.) +use fpm_strings, only: string_t, operator(.in.) use fpm_backend, only: build_package use fpm_command_line, only: fpm_build_settings, fpm_new_settings, & fpm_run_settings, fpm_install_settings, fpm_test_settings diff --git a/fpm/src/fpm_compiler.f90 b/fpm/src/fpm_compiler.f90 index 6336e4e..0265985 100644 --- a/fpm/src/fpm_compiler.f90 +++ b/fpm/src/fpm_compiler.f90 @@ -141,7 +141,6 @@ character(len=:),allocatable :: mandatory ! flags required for fpm to function p & -fp-model precise& & -pc 64& & -align all& - & -coarray& & -error-limit 1& & -reentrancy threaded& & -nogen-interfaces& @@ -153,7 +152,6 @@ character(len=:),allocatable :: mandatory ! flags required for fpm to function p fflags = '& & -warn all& & -check:all:noarg_temp_created& - & -coarray& & -error-limit 1& & -O0& & -g& diff --git a/fpm/src/fpm_source_parsing.f90 b/fpm/src/fpm_source_parsing.f90 index ea5b4f9..f81555d 100644 --- a/fpm/src/fpm_source_parsing.f90 +++ b/fpm/src/fpm_source_parsing.f90 @@ -16,7 +16,7 @@ !> module fpm_source_parsing use fpm_error, only: error_t, file_parse_error, fatal_error -use fpm_strings, only: string_t, split, lower, str_ends_with, fnv_1a +use fpm_strings, only: string_t, string_cat, split, lower, str_ends_with, fnv_1a use fpm_model, only: srcfile_t, & FPM_UNIT_UNKNOWN, FPM_UNIT_PROGRAM, FPM_UNIT_MODULE, & FPM_UNIT_SUBMODULE, FPM_UNIT_SUBPROGRAM, & @@ -86,6 +86,9 @@ function parse_f_source(f_filename,error) result(f_source) file_lines = read_lines(fh) close(fh) + ! Ignore empty files, returned as FPM_UNIT_UNKNOW + if (len_trim(string_cat(file_lines,' ')) < 1) return + f_source%digest = fnv_1a(file_lines) do pass = 1,2 @@ -197,8 +200,14 @@ function parse_f_source(f_filename,error) result(f_source) if (mod_name == 'procedure' .or. & mod_name == 'subroutine' .or. & - mod_name == 'function') then - ! Ignore these cases + mod_name == 'function' .or. & + scan(mod_name,'=(')>0 ) then + ! Ignore these cases: + ! module procedure * + ! module function * + ! module subroutine * + ! module =* + ! module (i) cycle end if @@ -275,7 +284,19 @@ function parse_f_source(f_filename,error) result(f_source) ! Detect if contains a program ! (no modules allowed after program def) - if (index(adjustl(lower(file_lines(i)%s)),'program') == 1) then + if (index(adjustl(lower(file_lines(i)%s)),'program ') == 1) then + + temp_string = lower(split_n(file_lines(i)%s,n=2,delims=' ',stat=stat)) + if (stat == 0) then + + if (scan(temp_string,'=(')>0 ) then + ! Ignore: + ! program =* + ! program (i) =* + cycle + end if + + end if f_source%unit_type = FPM_UNIT_PROGRAM @@ -370,6 +391,12 @@ function parse_c_source(c_filename,error) result(c_source) file_lines = read_lines(fh) close(fh) + ! Ignore empty files, returned as FPM_UNIT_UNKNOW + if (len_trim(string_cat(file_lines,' ')) < 1) then + c_source%unit_type = FPM_UNIT_UNKNOWN + return + end if + c_source%digest = fnv_1a(file_lines) do pass = 1,2 diff --git a/fpm/src/fpm_sources.f90 b/fpm/src/fpm_sources.f90 index de2df1c..3fffa81 100644 --- a/fpm/src/fpm_sources.f90 +++ b/fpm/src/fpm_sources.f90 @@ -15,6 +15,9 @@ implicit none private public :: add_sources_from_dir, add_executable_sources +character(4), parameter :: fortran_suffixes(2) = [".f90", & + ".f "] + contains !> Wrapper to source parsing routines. @@ -24,7 +27,7 @@ function parse_source(source_file_path,error) result(source) type(error_t), allocatable, intent(out) :: error type(srcfile_t) :: source - if (str_ends_with(lower(source_file_path), ".f90")) then + if (str_ends_with(lower(source_file_path), fortran_suffixes)) then source = parse_f_source(source_file_path, error) @@ -32,8 +35,7 @@ function parse_source(source_file_path,error) result(source) source%exe_name = basename(source_file_path,suffix=.false.) end if - else if (str_ends_with(lower(source_file_path), ".c") .or. & - str_ends_with(lower(source_file_path), ".h")) then + else if (str_ends_with(lower(source_file_path), [".c", ".h"])) then source = parse_c_source(source_file_path,error) @@ -80,9 +82,8 @@ subroutine add_sources_from_dir(sources,directory,scope,with_executables,recurse end if is_source = [(.not.(canon_path(file_names(i)%s) .in. existing_src_files) .and. & - (str_ends_with(lower(file_names(i)%s), ".f90") .or. & - str_ends_with(lower(file_names(i)%s), ".c") .or. & - str_ends_with(lower(file_names(i)%s), ".h") ),i=1,size(file_names))] + (str_ends_with(lower(file_names(i)%s), fortran_suffixes) .or. & + str_ends_with(lower(file_names(i)%s),[".c",".h"]) ),i=1,size(file_names))] src_file_names = pack(file_names,is_source) allocate(dir_sources(size(src_file_names))) diff --git a/fpm/src/fpm_strings.f90 b/fpm/src/fpm_strings.f90 index ea1db01..8e57d5b 100644 --- a/fpm/src/fpm_strings.f90 +++ b/fpm/src/fpm_strings.f90 @@ -24,9 +24,14 @@ interface fnv_1a procedure :: fnv_1a_string_t end interface fnv_1a +interface str_ends_with + procedure :: str_ends_with_str + procedure :: str_ends_with_any +end interface str_ends_with + contains -logical function str_ends_with(s, e) result(r) +pure logical function str_ends_with_str(s, e) result(r) character(*), intent(in) :: s, e integer :: n1, n2 n1 = len(s)-len(e)+1 @@ -36,7 +41,23 @@ logical function str_ends_with(s, e) result(r) else r = (s(n1:n2) == e) end if -end function str_ends_with +end function str_ends_with_str + +pure logical function str_ends_with_any(s, e) result(r) + character(*), intent(in) :: s + character(*), intent(in) :: e(:) + + integer :: i + + r = .true. + do i=1,size(e) + + if (str_ends_with(s,trim(e(i)))) return + + end do + r = .false. + +end function str_ends_with_any function f_string(c_string) use iso_c_binding diff --git a/fpm/test/fpm_test/test_source_parsing.f90 b/fpm/test/fpm_test/test_source_parsing.f90 index 4463c07..79a4d7a 100644 --- a/fpm/test/fpm_test/test_source_parsing.f90 +++ b/fpm/test/fpm_test/test_source_parsing.f90 @@ -24,6 +24,7 @@ contains & new_unittest("modules-used", test_modules_used), & & new_unittest("intrinsic-modules-used", test_intrinsic_modules_used), & & new_unittest("include-stmt", test_include_stmt), & + & new_unittest("program", test_program), & & new_unittest("module", test_module), & & new_unittest("program-with-module", test_program_with_module), & & new_unittest("submodule", test_submodule), & @@ -238,6 +239,61 @@ contains end subroutine test_include_stmt + !> Try to parse a simple fortran program + subroutine test_program(error) + + !> Error handling + type(error_t), allocatable, intent(out) :: error + + integer :: unit + character(:), allocatable :: temp_file + type(srcfile_t), allocatable :: f_source + + allocate(temp_file, source=get_temp_filename()) + + open(file=temp_file, newunit=unit) + write(unit, '(a)') & + & 'program my_program', & + & 'use module_one', & + & 'implicit none', & + & 'integer :: module', & + & 'module = 1', & + & 'module= 1', & + & 'module =1', & + & 'module (i) =1', & + & 'contains', & + & 'subroutine f()', & + & 'end subroutine f', & + & 'end program my_program' + close(unit) + + f_source = parse_f_source(temp_file,error) + if (allocated(error)) then + return + end if + + if (f_source%unit_type /= FPM_UNIT_PROGRAM) then + call test_failed(error,'Wrong unit type detected - expecting FPM_UNIT_PROGRAM') + return + end if + + if (size(f_source%modules_provided) /= 0) then + call test_failed(error,'Unexpected modules_provided - expecting zero') + return + end if + + if (size(f_source%modules_used) /= 1) then + call test_failed(error,'Incorrect number of modules_used - expecting one') + return + end if + + if (.not.('module_one' .in. f_source%modules_used)) then + call test_failed(error,'Missing module in modules_used') + return + end if + + end subroutine test_program + !> Try to parse fortran module subroutine test_module(error) @@ -258,6 +314,11 @@ contains & 'interface', & & ' module subroutine f()', & & 'end interface', & + & 'integer :: program', & + & 'program = 1', & + & 'program= 1', & + & 'program =1', & + & 'program (i) =1', & & 'contains', & & 'module procedure f()', & & 'end procedure f', & diff --git a/manifest-reference.md b/manifest-reference.md index 07235b9..8e9f65d 100644 --- a/manifest-reference.md +++ b/manifest-reference.md @@ -233,8 +233,6 @@ The source file containing the program body can be specified in the *main* entry Executables can have their own dependencies. See [specifying dependencies](#specifying-dependencies) for more details. -> Dependencies supported in Bootstrap fpm only - Executables can also specify their own external library dependencies. See [external libraries](#link-external-libraries) for more details. @@ -277,8 +275,6 @@ The source file containing the program body can be specified in the *main* entry Examples can have their own dependencies. See [specifying dependencies](#specifying-dependencies) for more details. -> Dependencies supported in Bootstrap fpm only - Examples can also specify their own external library dependencies. See [external libraries](#link-external-libraries) for more details. @@ -313,8 +309,6 @@ The source file containing the program body can be specified in the *main* entry Tests can have their own dependencies. See [specifying dependencies](#specifying-dependencies) for more details. -> Dependencies supported in Bootstrap fpm only - Tests can also specify their own external library dependencies. See [external libraries](#link-external-libraries) for more details. |