aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci/run_tests.bat3
-rwxr-xr-xci/run_tests.sh1
-rw-r--r--fpm/app/main.f9023
-rw-r--r--fpm/src/fpm.f90188
4 files changed, 195 insertions, 20 deletions
diff --git a/ci/run_tests.bat b/ci/run_tests.bat
index dcc9a8a..fc4e1f5 100755
--- a/ci/run_tests.bat
+++ b/ci/run_tests.bat
@@ -12,6 +12,9 @@ if errorlevel 1 exit 1
fpm run --args build
if errorlevel 1 exit 1
+.\fpm
+if errorlevel 1 exit 1
+
cd ..\test\example_packages\hello_world
if errorlevel 1 exit 1
diff --git a/ci/run_tests.sh b/ci/run_tests.sh
index 8f99a2c..e8a37e6 100755
--- a/ci/run_tests.sh
+++ b/ci/run_tests.sh
@@ -6,6 +6,7 @@ cd fpm
fpm build
fpm run
fpm run --args build
+./fpm
cd ../test/example_packages/hello_world
../../../fpm/fpm build
./hello_world
diff --git a/fpm/app/main.f90 b/fpm/app/main.f90
index 8533f8c..361cae5 100644
--- a/fpm/app/main.f90
+++ b/fpm/app/main.f90
@@ -1,5 +1,5 @@
program main
-use fpm, only: print_help, cmd_build
+use fpm, only: print_help, cmd_build, cmd_install, cmd_new, cmd_run, cmd_test
implicit none
character(100) :: cmdarg
@@ -7,14 +7,21 @@ if (command_argument_count() == 0) then
call print_help()
else if (command_argument_count() == 1) then
call get_command_argument(1, cmdarg)
- if (cmdarg == "build") then
- call cmd_build()
- else
- print *, "Unknown command: ", cmdarg
- error stop
- end if
+ select case(trim(cmdarg))
+ case("build")
+ call cmd_build()
+ case("install")
+ call cmd_install()
+ case("new")
+ call cmd_new()
+ case("run")
+ call cmd_run()
+ case default
+ print *, "fpm error: No such command " // trim(cmdarg)
+ error stop 1
+ end select
else
print *, "Too many arguments"
- error stop
+ error stop 1
end if
end program main
diff --git a/fpm/src/fpm.f90 b/fpm/src/fpm.f90
index 85597c0..aefbfd0 100644
--- a/fpm/src/fpm.f90
+++ b/fpm/src/fpm.f90
@@ -1,12 +1,129 @@
module fpm
implicit none
private
-public :: print_help, cmd_build
+public :: print_help, cmd_build, cmd_install, cmd_new, cmd_run, cmd_test
+
+integer, parameter :: OS_LINUX = 1
+integer, parameter :: OS_MACOS = 2
+integer, parameter :: OS_WINDOWS = 3
+
+type string_t
+ character(len=:), allocatable :: s
+end type
contains
+integer function get_os_type() result(r)
+! Determine the OS type
+!
+! Returns one of OS_LINUX, OS_MACOS, OS_WINDOWS.
+!
+! Currently we use the $HOME and $HOMEPATH environment variables to determine
+! the OS type. That is not 100% accurate in all cases, but it seems to be good
+! enough for now. See the following issue for a more robust solution:
+!
+! https://github.com/fortran-lang/fpm/issues/144
+!
+character(len=100) :: val
+integer stat
+! Only Windows define $HOMEPATH by default and we test its value to improve the
+! chances of it working even if a user defines $HOMEPATH on Linux or macOS.
+call get_environment_variable("HOMEPATH", val, status=stat)
+if (stat == 0 .and. val(1:7) == "\Users\") then
+ r = OS_WINDOWS
+ return
+end if
+
+! We assume that $HOME=/home/... is Linux, $HOME=/Users/... is macOS, otherwise
+! we assume Linux. This is only a heuristic and can easily fail.
+call get_environment_variable("HOME", val, status=stat)
+if (stat == 1) then
+ print *, "$HOME does not exist"
+ error stop
+end if
+if (stat /= 0) then
+ print *, "get_environment_variable() failed"
+ error stop
+end if
+if (val(1:6) == "/home/") then
+ r = OS_LINUX
+else if (val(1:7) == "/Users/") then
+ r = OS_MACOS
+else
+ ! This will happen on HPC systems that typically do not use either /home nor
+ ! /Users for $HOME. Those systems are typically Linux, so for now we simply
+ ! set Linux here.
+ r = OS_LINUX
+end if
+end function
+
+integer function number_of_rows(s) result(nrows)
+! determine number or rows
+integer,intent(in)::s
+integer :: ios
+character(len=100) :: r
+rewind(s)
+nrows = 0
+do
+ read(s, *, iostat=ios) r
+ if (ios /= 0) exit
+ nrows = nrows + 1
+end do
+rewind(s)
+end function
+
+
+subroutine list_files(dir, files)
+character(len=*), intent(in) :: dir
+type(string_t), allocatable, intent(out) :: files(:)
+character(len=100) :: filename
+integer :: stat, u, i
+! Using `inquire` / exists on directories works with gfortran, but not ifort
+if (.not. exists(dir)) then
+ allocate(files(0))
+ return
+end if
+select case (get_os_type())
+ case (OS_LINUX)
+ call execute_command_line("ls " // dir // " > fpm_ls.out", exitstat=stat)
+ case (OS_MACOS)
+ call execute_command_line("ls " // dir // " > fpm_ls.out", exitstat=stat)
+ case (OS_WINDOWS)
+ call execute_command_line("dir /b " // dir // " > fpm_ls.out", exitstat=stat)
+end select
+if (stat /= 0) then
+ print *, "execute_command_line() failed"
+ error stop
+end if
+open(newunit=u, file="fpm_ls.out", status="old")
+allocate(files(number_of_rows(u)))
+do i = 1, size(files)
+ read(u, *) filename
+ files(i)%s = trim(filename)
+end do
+close(u)
+end subroutine
+
subroutine print_help()
-print *, "Fortran Package Manager (fpm)"
+print *, "fpm - A Fortran package manager and build system"
+select case (get_os_type())
+ case (OS_LINUX)
+ print *, "OS Type: Linux"
+ case (OS_MACOS)
+ print *, "OS Type: macOS"
+ case (OS_WINDOWS)
+ print *, "OS Type: Windows"
+end select
+print *
+print *, "Usage:"
+print *, " fpm [COMMAND]"
+print *
+print *, "Valid fpm commands are:"
+print *, " build Compile the current package"
+print *, " install Install a Fortran binary or library (not implemented)"
+print *, " new Create a new Fortran package (not implemented)"
+print *, " run Run a binary of the local package (not implemented)"
+print *, " test Run the tests (not implemented)"
end subroutine
subroutine run(cmd)
@@ -25,19 +142,66 @@ character(len=*), intent(in) :: filename
inquire(file=filename, exist=r)
end function
-subroutine cmd_build()
-logical :: src
-print *, "# Building project"
-src = exists("src/fpm.f90")
-if (src) then
- call run("gfortran -c src/fpm.f90 -o fpm.o")
+logical function str_ends_with(s, e) result(r)
+character(*), intent(in) :: s, e
+integer :: n1, n2
+n1 = len(s)-len(e)+1
+n2 = len(s)
+if (n1 < 1) then
+ r = .false.
+else
+ r = (s(n1:n2) == e)
end if
-call run("gfortran -c app/main.f90 -o main.o")
-if (src) then
- call run("gfortran main.o fpm.o -o fpm")
+end function
+
+subroutine package_name(name)
+character(:), allocatable, intent(out) :: name
+! Currrently a heuristic. We should update this to read the name from fpm.toml
+if (exists("src/fpm.f90")) then
+ name = "fpm"
else
- call run("gfortran main.o -o hello_world")
+ name = "hello_world"
end if
end subroutine
+subroutine cmd_build()
+type(string_t), allocatable :: files(:)
+character(:), allocatable :: basename, pkg_name, linking
+integer :: i, n
+print *, "# Building project"
+call list_files("src", files)
+linking = ""
+do i = 1, size(files)
+ if (str_ends_with(files(i)%s, ".f90")) then
+ n = len(files(i)%s)
+ basename = files(i)%s(1:n-4)
+ call run("gfortran -c src/" // basename // ".f90 -o " // basename // ".o")
+ linking = linking // " " // basename // ".o"
+ end if
+end do
+call run("gfortran -c app/main.f90 -o main.o")
+call package_name(pkg_name)
+call run("gfortran main.o " // linking // " -o " // pkg_name)
+end subroutine
+
+subroutine cmd_install()
+ print *, "fpm error: 'fpm install' not implemented."
+ error stop 1
+end subroutine
+
+subroutine cmd_new()
+ print *, "fpm error: 'fpm new' not implemented."
+ error stop 1
+end subroutine
+
+subroutine cmd_run()
+ print *, "fpm error: 'fpm run' not implemented."
+ error stop 1
+end subroutine
+
+subroutine cmd_test()
+ print *, "fpm error: 'fpm test' not implemented."
+ error stop 1
+end subroutine
+
end module fpm