Skip to content

Enable fpm to create a new package #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ stack install

On Linux, the above command installs `fpm` to `${HOME}/.local/bin`.

### Creating a new project

Creating a new fpm project is as simple as running the command `fpm new project_name`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Creating a new fpm project is as simple as running the command `fpm new project_name`.
Creating a new fpm project is as simple as running the command `fpm new <project-name>`.

This will create a new folder in your current directory with the following contents
and initialized as a git repository.

* `fpm.toml` with your project's name and some default standard meta-data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `fpm.toml` with your project's name and some default standard meta-data
* `fpm.toml` with your project's name and some default standard metadata

* `README.md` with your project's name
* `.gitgnore`
* `src/project_name.f90` with a simple hello world subroutine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `src/project_name.f90` with a simple hello world subroutine
* `src/<project-name>.f90` with a module containing a simple hello world subroutine

* `app/main.f90` (if `--with-executable` flag used) a program that calls the subroutine
* `test/main.f90` (if `--with-test` flag used) an empty test program
Comment on lines +66 to +67
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be tackled in a separate PR, but I suggest we use --bin instead of --with-executable (and also make it the default). Also, add an empty test program by default, as this will encourage all new projects to have tests.


### Building your Fortran project with fpm

fpm understands the basic commands:
Expand Down
111 changes: 100 additions & 11 deletions src/Fpm.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import Build ( buildLibrary
, buildProgram
, buildWithScript
)
import Control.Monad.Extra ( concatMapM )
import Control.Monad.Extra ( concatMapM
, when
)
import Data.List ( isSuffixOf
, find
, nub
Expand Down Expand Up @@ -47,7 +49,8 @@ import Options.Applicative ( Parser
, switch
, value
)
import System.Directory ( doesDirectoryExist
import System.Directory ( createDirectory
, doesDirectoryExist
, doesFileExist
, makeAbsolute
, withCurrentDirectory
Expand Down Expand Up @@ -100,7 +103,7 @@ data GitRef = Tag String | Branch String | Commit String deriving Show

data PathVersionSpec = PathVersionSpec { pathVersionSpecPath :: String } deriving Show

data Command = Run String | Test String | Build
data Command = Run String | Test String | Build | New String Bool Bool

data DependencyTree = Dependency {
dependencyName :: String
Expand All @@ -111,14 +114,17 @@ data DependencyTree = Dependency {
}

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
start args = case command' args of
New projectName withExecutable withTest ->
createNewProject projectName withExecutable withTest
_ -> 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
Expand Down Expand Up @@ -279,6 +285,8 @@ arguments =
<> command "test" (info testArguments (progDesc "Run the tests"))
<> command "build"
(info buildArguments (progDesc "Build the executable"))
<> command "new"
(info newArguments (progDesc "Create a new project in a new directory"))
)
<*> switch (long "release" <> help "Build in release mode")
<*> strOption
Expand All @@ -297,6 +305,13 @@ testArguments =
buildArguments :: Parser Command
buildArguments = pure Build

newArguments :: Parser Command
newArguments =
New
<$> strArgument (metavar "NAME" <> help "Name of new project")
<*> switch (long "with-executable" <> help "Include an executable")
<*> switch (long "with-test" <> help "Include a test")

getDirectoriesFiles :: [FilePath] -> [FilePattern] -> IO [FilePath]
getDirectoriesFiles dirs exts = getDirectoryFilesIO "" newPatterns
where
Expand Down Expand Up @@ -629,3 +644,77 @@ buildDependency buildPrefix compiler flags (Dependency name path sourcePath mBui
name
(map fst transitiveDependencies)
return $ (buildPath, thisArchive) : transitiveDependencies

createNewProject :: String -> Bool -> Bool -> IO ()
createNewProject projectName withExecutable withTest = do
createDirectory projectName
writeFile (projectName </> "fpm.toml") (templateFpmToml projectName)
writeFile (projectName </> "README.md") (templateReadme projectName)
writeFile (projectName </> ".gitignore") "build/*\n"
createDirectory (projectName </> "src")
writeFile (projectName </> "src" </> projectName <.> "f90")
(templateModule projectName)
when withExecutable $ do
createDirectory (projectName </> "app")
writeFile (projectName </> "app" </> "main.f90")
(templateProgram projectName)
when withTest $ do
createDirectory (projectName </> "test")
writeFile (projectName </> "test" </> "main.f90") templateTest
withCurrentDirectory projectName $ do
system "git init"
return ()

templateFpmToml :: String -> String
templateFpmToml projectName =
"name = \""
++ projectName
++ "\"\n"
++ "version = \"0.1.0\"\n"
++ "license = \"license\"\n"
++ "author = \"Jane Doe\"\n"
++ "maintainer = \"[email protected]\"\n"
++ "copyright = \"2020 Jane Doe\"\n"

templateModule :: String -> String
templateModule projectName =
"module "
++ projectName
++ "\n"
++ " implicit none\n"
++ " private\n"
++ "\n"
++ " public :: say_hello\n"
++ "contains\n"
++ " subroutine say_hello\n"
++ " print *, \"Hello, "
++ projectName
++ "!\"\n"
++ " end subroutine say_hello\n"
++ "end module "
++ projectName
++ "\n"

templateReadme :: String -> String
templateReadme projectName =
"# " ++ projectName ++ "\n" ++ "\n" ++ "My cool new project!\n"

templateProgram :: String -> String
templateProgram projectName =
"program main\n"
++ " use "
++ projectName
++ ", only: say_hello\n"
++ "\n"
++ " implicit none\n"
++ "\n"
++ " call say_hello\n"
++ "end program main\n"

templateTest :: String
templateTest =
"program main\n"
++ " implicit none\n"
++ "\n"
++ " print *, \"Put some tests in here!\"\n"
++ "end program main\n"