aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Richardson <brichardson@structint.com>2020-04-13 19:31:08 -0500
committerBrad Richardson <brichardson@structint.com>2020-04-29 19:08:05 -0700
commitb07a5fcc4d9ea577e0918f683ba2fc1d82e55cd9 (patch)
treefc5dd9905f38d37807d8e1f2e7436f7a2501b0da
parenteafba7a95131c34821c7bb725f1e26552c01271b (diff)
downloadfpm-b07a5fcc4d9ea577e0918f683ba2fc1d82e55cd9.tar.gz
fpm-b07a5fcc4d9ea577e0918f683ba2fc1d82e55cd9.zip
Enable running the tests without external calls
-rw-r--r--app/Main.hs294
-rw-r--r--src/Fpm.hs301
-rw-r--r--test/Spec.hs9
3 files changed, 310 insertions, 294 deletions
diff --git a/app/Main.hs b/app/Main.hs
index 38ba8f1..4897901 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,296 +1,8 @@
-{-# LANGUAGE OverloadedStrings #-}
-
module Main where
-import Build ( buildLibrary
- , buildProgram
- )
-import qualified Data.Text.IO as TIO
-import Development.Shake ( FilePattern
- , (<//>)
- , getDirectoryFilesIO
- )
-import Development.Shake.FilePath ( (</>) )
-import Options.Applicative ( Parser
- , (<**>)
- , command
- , execParser
- , fullDesc
- , header
- , help
- , helper
- , info
- , long
- , progDesc
- , subparser
- , switch
- )
-import System.Directory ( doesDirectoryExist
- , doesFileExist
+import Fpm ( getArguments
+ , start
)
-import System.Process ( runCommand )
-import Toml ( TomlCodec
- , (.=)
- )
-import qualified Toml
-
-data Arguments = Arguments { command' :: Command, release :: Bool }
-
-data TomlSettings = TomlSettings {
- tomlSettingsCompiler :: String
- , tomlSettingsProjectName :: String
- , tomlSettingsLibrary :: (Maybe Library)
- , tomlSettingsExecutables :: [Executable]
- , tomlSettingsTests :: [Executable]
-}
-
-data AppSettings = AppSettings {
- appSettingsCompiler :: String
- , appSettingsProjectName :: String
- , appSettingsBuildPrefix :: String
- , appSettingsFlags :: [String]
- , appSettingsLibrary :: (Maybe Library)
- , appSettingsExecutables :: [Executable]
- , appSettingsTests :: [Executable]
-}
-
-data Library = Library { librarySourceDir :: String }
-
-data Executable = Executable {
- executableSourceDir :: String
- , executableMainFile :: String
- , executableName :: String
-}
-
-data Command = Run | Test | Build
main :: IO ()
-main = do
- args <- getArguments
- fpmContents <- TIO.readFile "fpm.toml"
- let tomlSettings = Toml.decode settingsCodec fpmContents
- case tomlSettings of
- Left err -> print err
- Right tomlSettings' -> do
- appSettings <- toml2AppSettings tomlSettings' (release args)
- app args appSettings
-
-app :: Arguments -> AppSettings -> IO ()
-app args settings = case command' args of
- Build -> build settings
- Run -> do
- build settings
- let buildPrefix = appSettingsBuildPrefix settings
- let
- executableNames = map
- (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
- sourceDir </> name
- )
- (appSettingsExecutables settings)
- let executables = map (buildPrefix </>) executableNames
- mapM_ runCommand executables
- Test -> do
- build settings
- let buildPrefix = appSettingsBuildPrefix settings
- let
- executableNames = map
- (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
- sourceDir </> name
- )
- (appSettingsTests settings)
- let executables = map (buildPrefix </>) executableNames
- mapM_ runCommand executables
-
-build :: AppSettings -> IO ()
-build settings = do
- let compiler = appSettingsCompiler settings
- let projectName = appSettingsProjectName settings
- let buildPrefix = appSettingsBuildPrefix settings
- let flags = appSettingsFlags settings
- let executables = appSettingsExecutables settings
- let tests = appSettingsTests settings
- executableDepends <- case appSettingsLibrary settings of
- Just librarySettings -> do
- let librarySourceDir' = librarySourceDir librarySettings
- buildLibrary librarySourceDir'
- [".f90", ".f", ".F", ".F90", ".f95", ".f03"]
- (buildPrefix </> "library")
- compiler
- flags
- projectName
- []
- return [buildPrefix </> "library"]
- Nothing -> do
- return []
- mapM_
- (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
- do
- buildProgram sourceDir
- executableDepends
- [".f90", ".f", ".F", ".F90", ".f95", ".f03"]
- (buildPrefix </> sourceDir)
- compiler
- flags
- name
- mainFile
- )
- executables
- mapM_
- (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
- do
- buildProgram sourceDir
- executableDepends
- [".f90", ".f", ".F", ".F90", ".f95", ".f03"]
- (buildPrefix </> sourceDir)
- compiler
- flags
- name
- mainFile
- )
- tests
-
-getArguments :: IO Arguments
-getArguments = execParser
- (info
- (arguments <**> helper)
- (fullDesc <> progDesc "Work with Fortran projects" <> header
- "fpm - A Fortran package manager and build system"
- )
- )
-
-arguments :: Parser Arguments
-arguments =
- Arguments
- <$> subparser
- ( command "run" (info runArguments (progDesc "Run the executable"))
- <> command "test" (info testArguments (progDesc "Run the tests"))
- <> command "build"
- (info buildArguments (progDesc "Build the executable"))
- )
- <*> switch (long "release" <> help "Build in release mode")
-
-runArguments :: Parser Command
-runArguments = pure Run
-
-testArguments :: Parser Command
-testArguments = pure Test
-
-buildArguments :: Parser Command
-buildArguments = pure Build
-
-getDirectoriesFiles :: [FilePath] -> [FilePattern] -> IO [FilePath]
-getDirectoriesFiles dirs exts = getDirectoryFilesIO "" newPatterns
- where
- newPatterns = concatMap appendExts dirs
- appendExts dir = map ((dir <//> "*") ++) exts
-
-settingsCodec :: TomlCodec TomlSettings
-settingsCodec =
- TomlSettings
- <$> Toml.string "compiler"
- .= tomlSettingsCompiler
- <*> Toml.string "name"
- .= tomlSettingsProjectName
- <*> Toml.dioptional (Toml.table libraryCodec "library")
- .= tomlSettingsLibrary
- <*> Toml.list executableCodec "executable"
- .= tomlSettingsExecutables
- <*> Toml.list executableCodec "test"
- .= tomlSettingsTests
-
-libraryCodec :: TomlCodec Library
-libraryCodec = Library <$> Toml.string "source-dir" .= librarySourceDir
-
-executableCodec :: TomlCodec Executable
-executableCodec =
- Executable
- <$> Toml.string "source-dir"
- .= executableSourceDir
- <*> Toml.string "main"
- .= executableMainFile
- <*> Toml.string "name"
- .= executableName
-
-toml2AppSettings :: TomlSettings -> Bool -> IO AppSettings
-toml2AppSettings tomlSettings release = do
- let projectName = tomlSettingsProjectName tomlSettings
- librarySettings <- getLibrarySettings $ tomlSettingsLibrary tomlSettings
- executableSettings <- getExecutableSettings
- (tomlSettingsExecutables tomlSettings)
- projectName
- testSettings <- getTestSettings $ tomlSettingsTests tomlSettings
- return AppSettings
- { appSettingsCompiler = tomlSettingsCompiler tomlSettings
- , appSettingsProjectName = projectName
- , appSettingsBuildPrefix = "build"
- </> if release then "release" else "debug"
- , appSettingsFlags = if release
- then
- [ "-Wall"
- , "-Wextra"
- , "-Wimplicit-interface"
- , "-fPIC"
- , "-fmax-errors=1"
- , "-O3"
- , "-march=native"
- , "-ffast-math"
- , "-funroll-loops"
- ]
- else
- [ "-Wall"
- , "-Wextra"
- , "-Wimplicit-interface"
- , "-fPIC"
- , "-fmax-errors=1"
- , "-g"
- , "-fbounds-check"
- , "-fcheck-array-temporaries"
- , "-fbacktrace"
- ]
- , appSettingsLibrary = librarySettings
- , appSettingsExecutables = executableSettings
- , appSettingsTests = testSettings
- }
-
-getLibrarySettings :: Maybe Library -> IO (Maybe Library)
-getLibrarySettings maybeSettings = case maybeSettings of
- Just settings -> return maybeSettings
- Nothing -> do
- defaultExists <- doesDirectoryExist "src"
- if defaultExists
- then return (Just (Library { librarySourceDir = "src" }))
- else return Nothing
-
-getExecutableSettings :: [Executable] -> String -> IO [Executable]
-getExecutableSettings [] projectName = do
- defaultDirectoryExists <- doesDirectoryExist "app"
- if defaultDirectoryExists
- then do
- defaultMainExists <- doesFileExist ("app" </> "main.f90")
- if defaultMainExists
- then return
- [ Executable { executableSourceDir = "app"
- , executableMainFile = "main.f90"
- , executableName = projectName
- }
- ]
- else return []
- else return []
-getExecutableSettings executables _ = return executables
-
-getTestSettings :: [Executable] -> IO [Executable]
-getTestSettings [] = do
- defaultDirectoryExists <- doesDirectoryExist "test"
- if defaultDirectoryExists
- then do
- defaultMainExists <- doesFileExist ("test" </> "main.f90")
- if defaultMainExists
- then return
- [ Executable { executableSourceDir = "test"
- , executableMainFile = "main.f90"
- , executableName = "runTests"
- }
- ]
- else return []
- else return []
-getTestSettings tests = return tests
+main = getArguments >>= start
diff --git a/src/Fpm.hs b/src/Fpm.hs
new file mode 100644
index 0000000..c3c3794
--- /dev/null
+++ b/src/Fpm.hs
@@ -0,0 +1,301 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Fpm
+ ( Arguments(..)
+ , Command(..)
+ , getArguments
+ , start
+ )
+where
+
+import Build ( buildLibrary
+ , buildProgram
+ )
+import qualified Data.Text.IO as TIO
+import Development.Shake ( FilePattern
+ , (<//>)
+ , getDirectoryFilesIO
+ )
+import Development.Shake.FilePath ( (</>) )
+import Options.Applicative ( Parser
+ , (<**>)
+ , command
+ , execParser
+ , fullDesc
+ , header
+ , help
+ , helper
+ , info
+ , long
+ , progDesc
+ , subparser
+ , switch
+ )
+import System.Directory ( doesDirectoryExist
+ , doesFileExist
+ )
+import System.Process ( runCommand )
+import Toml ( TomlCodec
+ , (.=)
+ )
+import qualified Toml
+
+data Arguments = Arguments { command' :: Command, release :: Bool }
+
+data TomlSettings = TomlSettings {
+ tomlSettingsCompiler :: String
+ , tomlSettingsProjectName :: String
+ , tomlSettingsLibrary :: (Maybe Library)
+ , tomlSettingsExecutables :: [Executable]
+ , tomlSettingsTests :: [Executable]
+}
+
+data AppSettings = AppSettings {
+ appSettingsCompiler :: String
+ , appSettingsProjectName :: String
+ , appSettingsBuildPrefix :: String
+ , appSettingsFlags :: [String]
+ , appSettingsLibrary :: (Maybe Library)
+ , appSettingsExecutables :: [Executable]
+ , appSettingsTests :: [Executable]
+}
+
+data Library = Library { librarySourceDir :: String }
+
+data Executable = Executable {
+ executableSourceDir :: String
+ , executableMainFile :: String
+ , executableName :: String
+}
+
+data Command = Run | Test | Build
+
+start :: Arguments -> IO ()
+start args = do
+ fpmContents <- TIO.readFile "fpm.toml"
+ let tomlSettings = Toml.decode settingsCodec fpmContents
+ case tomlSettings of
+ Left err -> print err
+ Right tomlSettings' -> do
+ appSettings <- toml2AppSettings tomlSettings' (release args)
+ app args appSettings
+
+app :: Arguments -> AppSettings -> IO ()
+app args settings = case command' args of
+ Build -> build settings
+ Run -> do
+ build settings
+ let buildPrefix = appSettingsBuildPrefix settings
+ let
+ executableNames = map
+ (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
+ sourceDir </> name
+ )
+ (appSettingsExecutables settings)
+ let executables = map (buildPrefix </>) executableNames
+ mapM_ runCommand executables
+ Test -> do
+ build settings
+ let buildPrefix = appSettingsBuildPrefix settings
+ let
+ executableNames = map
+ (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
+ sourceDir </> name
+ )
+ (appSettingsTests settings)
+ let executables = map (buildPrefix </>) executableNames
+ mapM_ runCommand executables
+
+build :: AppSettings -> IO ()
+build settings = do
+ let compiler = appSettingsCompiler settings
+ let projectName = appSettingsProjectName settings
+ let buildPrefix = appSettingsBuildPrefix settings
+ let flags = appSettingsFlags settings
+ let executables = appSettingsExecutables settings
+ let tests = appSettingsTests settings
+ executableDepends <- case appSettingsLibrary settings of
+ Just librarySettings -> do
+ let librarySourceDir' = librarySourceDir librarySettings
+ buildLibrary librarySourceDir'
+ [".f90", ".f", ".F", ".F90", ".f95", ".f03"]
+ (buildPrefix </> "library")
+ compiler
+ flags
+ projectName
+ []
+ return [buildPrefix </> "library"]
+ Nothing -> do
+ return []
+ mapM_
+ (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
+ do
+ buildProgram sourceDir
+ executableDepends
+ [".f90", ".f", ".F", ".F90", ".f95", ".f03"]
+ (buildPrefix </> sourceDir)
+ compiler
+ flags
+ name
+ mainFile
+ )
+ executables
+ mapM_
+ (\Executable { executableSourceDir = sourceDir, executableMainFile = mainFile, executableName = name } ->
+ do
+ buildProgram sourceDir
+ executableDepends
+ [".f90", ".f", ".F", ".F90", ".f95", ".f03"]
+ (buildPrefix </> sourceDir)
+ compiler
+ flags
+ name
+ mainFile
+ )
+ tests
+
+getArguments :: IO Arguments
+getArguments = execParser
+ (info
+ (arguments <**> helper)
+ (fullDesc <> progDesc "Work with Fortran projects" <> header
+ "fpm - A Fortran package manager and build system"
+ )
+ )
+
+arguments :: Parser Arguments
+arguments =
+ Arguments
+ <$> subparser
+ ( command "run" (info runArguments (progDesc "Run the executable"))
+ <> command "test" (info testArguments (progDesc "Run the tests"))
+ <> command "build"
+ (info buildArguments (progDesc "Build the executable"))
+ )
+ <*> switch (long "release" <> help "Build in release mode")
+
+runArguments :: Parser Command
+runArguments = pure Run
+
+testArguments :: Parser Command
+testArguments = pure Test
+
+buildArguments :: Parser Command
+buildArguments = pure Build
+
+getDirectoriesFiles :: [FilePath] -> [FilePattern] -> IO [FilePath]
+getDirectoriesFiles dirs exts = getDirectoryFilesIO "" newPatterns
+ where
+ newPatterns = concatMap appendExts dirs
+ appendExts dir = map ((dir <//> "*") ++) exts
+
+settingsCodec :: TomlCodec TomlSettings
+settingsCodec =
+ TomlSettings
+ <$> Toml.string "compiler"
+ .= tomlSettingsCompiler
+ <*> Toml.string "name"
+ .= tomlSettingsProjectName
+ <*> Toml.dioptional (Toml.table libraryCodec "library")
+ .= tomlSettingsLibrary
+ <*> Toml.list executableCodec "executable"
+ .= tomlSettingsExecutables
+ <*> Toml.list executableCodec "test"
+ .= tomlSettingsTests
+
+libraryCodec :: TomlCodec Library
+libraryCodec = Library <$> Toml.string "source-dir" .= librarySourceDir
+
+executableCodec :: TomlCodec Executable
+executableCodec =
+ Executable
+ <$> Toml.string "source-dir"
+ .= executableSourceDir
+ <*> Toml.string "main"
+ .= executableMainFile
+ <*> Toml.string "name"
+ .= executableName
+
+toml2AppSettings :: TomlSettings -> Bool -> IO AppSettings
+toml2AppSettings tomlSettings release = do
+ let projectName = tomlSettingsProjectName tomlSettings
+ librarySettings <- getLibrarySettings $ tomlSettingsLibrary tomlSettings
+ executableSettings <- getExecutableSettings
+ (tomlSettingsExecutables tomlSettings)
+ projectName
+ testSettings <- getTestSettings $ tomlSettingsTests tomlSettings
+ return AppSettings
+ { appSettingsCompiler = tomlSettingsCompiler tomlSettings
+ , appSettingsProjectName = projectName
+ , appSettingsBuildPrefix = "build"
+ </> if release then "release" else "debug"
+ , appSettingsFlags = if release
+ then
+ [ "-Wall"
+ , "-Wextra"
+ , "-Wimplicit-interface"
+ , "-fPIC"
+ , "-fmax-errors=1"
+ , "-O3"
+ , "-march=native"
+ , "-ffast-math"
+ , "-funroll-loops"
+ ]
+ else
+ [ "-Wall"
+ , "-Wextra"
+ , "-Wimplicit-interface"
+ , "-fPIC"
+ , "-fmax-errors=1"
+ , "-g"
+ , "-fbounds-check"
+ , "-fcheck-array-temporaries"
+ , "-fbacktrace"
+ ]
+ , appSettingsLibrary = librarySettings
+ , appSettingsExecutables = executableSettings
+ , appSettingsTests = testSettings
+ }
+
+getLibrarySettings :: Maybe Library -> IO (Maybe Library)
+getLibrarySettings maybeSettings = case maybeSettings of
+ Just settings -> return maybeSettings
+ Nothing -> do
+ defaultExists <- doesDirectoryExist "src"
+ if defaultExists
+ then return (Just (Library { librarySourceDir = "src" }))
+ else return Nothing
+
+getExecutableSettings :: [Executable] -> String -> IO [Executable]
+getExecutableSettings [] projectName = do
+ defaultDirectoryExists <- doesDirectoryExist "app"
+ if defaultDirectoryExists
+ then do
+ defaultMainExists <- doesFileExist ("app" </> "main.f90")
+ if defaultMainExists
+ then return
+ [ Executable { executableSourceDir = "app"
+ , executableMainFile = "main.f90"
+ , executableName = projectName
+ }
+ ]
+ else return []
+ else return []
+getExecutableSettings executables _ = return executables
+
+getTestSettings :: [Executable] -> IO [Executable]
+getTestSettings [] = do
+ defaultDirectoryExists <- doesDirectoryExist "test"
+ if defaultDirectoryExists
+ then do
+ defaultMainExists <- doesFileExist ("test" </> "main.f90")
+ if defaultMainExists
+ then return
+ [ Executable { executableSourceDir = "test"
+ , executableMainFile = "main.f90"
+ , executableName = "runTests"
+ }
+ ]
+ else return []
+ else return []
+getTestSettings tests = return tests
diff --git a/test/Spec.hs b/test/Spec.hs
index 2fd2364..ccb3b24 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,6 +1,9 @@
import Development.Shake.FilePath ( (</>) )
+import Fpm ( Arguments(..)
+ , Command(..)
+ , start
+ )
import System.Directory ( withCurrentDirectory )
-import System.Process ( callCommand )
main :: IO ()
main = do
@@ -8,8 +11,8 @@ main = do
testExampleProject :: IO ()
testExampleProject =
- withCurrentDirectory "example_project" $ callCommand "stack run -- build"
+ withCurrentDirectory "example_project" $ start $ Arguments Build False
testHelloWorld :: IO ()
testHelloWorld =
- withCurrentDirectory "hello_world" $ callCommand "stack run -- run"
+ withCurrentDirectory "hello_world" $ start $ Arguments Run False