-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 28f0da6
Showing
10 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
version: 2 | ||
updates: | ||
- package-ecosystem: "github-actions" | ||
directory: "/" | ||
schedule: | ||
interval: "monthly" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2024 Minh Dao | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# run-f | ||
|
||
This simple Fortran library allows you to execute a command in the command line and save its result as a string without the need for a temporary file. | ||
|
||
It was inspired by the work of [Jacob Williams](https://degenerateconic.com/fortran-c-interoperability.html) and uses `iso_c_binding` to call `popen`, `fgets` and `pclose` from the C standard library. | ||
|
||
## Usage | ||
|
||
First, import the `run_f` module into your Fortran code: | ||
|
||
```fortran | ||
use run_f, only: run | ||
``` | ||
|
||
Then you can use the `run` function to execute a command and save its result as a string: | ||
|
||
```fortran | ||
character(len=:), allocatable :: output | ||
output = run("whoami") | ||
``` | ||
|
||
### Error Handling | ||
|
||
### Print Command | ||
|
||
### Ignore Errors | ||
|
||
You can also use the `optional` arguments to add error handling or print the command to the console before executing it: | ||
|
||
```fortran | ||
character(:), allocatable :: output | ||
character(*), parameter :: command = "whoami" | ||
logical :: has_error | ||
output = run(command, has_error, print_cmd=.true.) | ||
if (has_error) then | ||
print *, "An error occurred while executing the command: ", command | ||
stop 1 | ||
end if | ||
``` | ||
|
||
## Install | ||
|
||
### fpm | ||
|
||
Using [fpm](https://fpm.fortran-lang.org/en/index.html), you can simply add this package as a dependency to your `fpm.toml` file: | ||
|
||
```toml | ||
[dependencies] | ||
|
||
[dependencies.run-f] | ||
git = "https://github.com/minhqdao/run-f.git" | ||
tag = "v0.1.0" | ||
``` | ||
|
||
Then import the `run_f` module into your Fortran code: | ||
|
||
```fortran | ||
use run_f, only: run | ||
``` | ||
|
||
Run `fpm build` to download the dependency. | ||
|
||
## Tests | ||
|
||
Run tests with: | ||
|
||
```bash | ||
fpm test | ||
``` | ||
|
||
## Formatting | ||
|
||
The CI will fail if the code is not formatted correctly. Please configure your editor to use [fprettify](https://pypi.org/project/fprettify/) and use an indentation width of 2 or run `fprettify -i 2 -r .` before committing. | ||
|
||
## Contribute | ||
|
||
Feel free to [create an issue](https://github.com/minhqdao/run-f/issues) in case you found a bug, have any questions or want to propose further improvements. Please stick to the existing coding style when opening a pull request. | ||
|
||
## License | ||
|
||
You can use, redistribute and/or modify the code under the terms of the [MIT License](https://github.com/minhqdao/run-f/blob/main/LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
name: CI | ||
on: push | ||
|
||
jobs: | ||
test: | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
os: [ubuntu-latest, macos-latest, windows-latest] | ||
toolchain: | ||
- { compiler: gcc, version: 13 } | ||
- { compiler: intel, version: 2024.1 } | ||
- { compiler: intel-classic, version: 2021.10 } | ||
- { compiler: lfortran, version: 0.33.0 } | ||
exclude: | ||
- os: macos-latest | ||
toolchain: { compiler: intel, version: 2024.1 } | ||
include: | ||
- os: ubuntu-latest | ||
toolchain: { compiler: nvidia-hpc, version: 23.11 } | ||
runs-on: ${{ matrix.os }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: fortran-lang/setup-fortran@v1 | ||
id: setup-fortran | ||
with: | ||
compiler: ${{ matrix.toolchain.compiler }} | ||
version: ${{ matrix.toolchain.version }} | ||
- uses: fortran-lang/setup-fpm@v5 | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
- run: fpm test | ||
|
||
msys2: | ||
runs-on: windows-latest | ||
strategy: | ||
matrix: | ||
msystem: [msys, ucrt64, mingw64, clang64] | ||
defaults: | ||
run: | ||
shell: msys2 {0} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: msys2/setup-msys2@v2 | ||
- uses: fortran-lang/setup-fortran@v1 | ||
with: | ||
compiler: gcc | ||
version: 13 | ||
- uses: fortran-lang/setup-fpm@v5 | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
- run: fpm test | ||
|
||
format: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-python@v5 | ||
with: | ||
python-version: "3.12" | ||
- name: Check formatting | ||
run: | | ||
pip install fprettify | ||
diff=$(fprettify -i 2 -r . -d) | ||
if [[ $diff ]]; then | ||
red="\033[0;31m" | ||
cyan="\033[0;36m" | ||
reset="\033[0m" | ||
printf -- "$diff\n" | ||
printf "${red}The code is not correctly formatted. Please run: ${reset}\n" | ||
printf "${cyan}fprettify -i 2 -r .${reset}\n" | ||
exit 1 | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
program ex_1 | ||
use run_f, only: run | ||
implicit none | ||
|
||
character(:), allocatable :: output | ||
|
||
output = run('whoami') | ||
print *, output | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
program ex_2 | ||
use run_f, only: run | ||
implicit none | ||
|
||
character(:), allocatable :: output | ||
character(*), parameter :: command = 'xyzabc' | ||
logical :: has_error | ||
|
||
output = run(command, has_error, print_cmd=.true.) | ||
|
||
if (has_error) then | ||
print *, 'Error executing command: ', command | ||
stop 1 | ||
end if | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
name = "run-f" | ||
version = "0.1.0" | ||
license = "MIT" | ||
author = "Minh Dao" | ||
maintainer = "[email protected]" | ||
copyright = "Copyright 2024, Minh Dao" | ||
|
||
[build] | ||
module-naming = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
module run_f | ||
use, intrinsic :: iso_c_binding, only: c_ptr, c_char, c_int, c_null_ptr, & | ||
& c_null_char, c_associated | ||
implicit none | ||
private | ||
|
||
public :: run | ||
|
||
interface | ||
function popen(cmd, mode) bind(C, name='popen') | ||
import :: c_char, c_ptr | ||
character(kind=c_char), dimension(*) :: cmd | ||
character(kind=c_char), dimension(*) :: mode | ||
type(c_ptr) :: popen | ||
end | ||
|
||
function fgets(str, size, stream) bind(C, name='fgets') | ||
import :: c_char, c_ptr, c_int | ||
character(kind=c_char), dimension(*) :: str | ||
integer(kind=c_int), value :: size | ||
type(c_ptr), value :: stream | ||
type(c_ptr) :: fgets | ||
end | ||
|
||
function pclose(stream) bind(C, name='pclose') | ||
import :: c_ptr, c_int | ||
type(c_ptr), value :: stream | ||
integer(c_int) :: pclose | ||
end | ||
end interface | ||
|
||
contains | ||
|
||
!> Convert a C string to a Fortran string. | ||
pure function c2f(c) result(f) | ||
character(len=*), intent(in) :: c | ||
character(len=:), allocatable :: f | ||
|
||
integer :: i | ||
|
||
i = index(c, c_null_char) | ||
|
||
if (i <= 0) then | ||
f = c | ||
else if (i == 1) then | ||
f = '' | ||
else if (i > 1) then | ||
f = c(1:i - 1) | ||
end if | ||
end | ||
|
||
!> Run a command in the command line and return the result as a string. | ||
function run(cmd, has_error, print_cmd) result(str) | ||
!> The command to run in the command line. | ||
character(len=*), intent(in) :: cmd | ||
|
||
!> True if running the command results in an error. | ||
logical, optional, intent(out) :: has_error | ||
|
||
!> Whether the command is printed to the command line before it is executed. | ||
logical, optional, intent(in) :: print_cmd | ||
|
||
!> Stores the result of the command. | ||
character(len=:), allocatable :: str | ||
|
||
integer, parameter :: buffer_length = 1024 | ||
|
||
type(c_ptr) :: handle | ||
integer(c_int) :: istat | ||
character(kind=c_char, len=buffer_length) :: line | ||
|
||
if (present(print_cmd)) then | ||
if (print_cmd) print *, 'Running command: ', cmd | ||
end if | ||
|
||
if (present(has_error)) has_error = .false. | ||
|
||
str = '' | ||
handle = c_null_ptr | ||
handle = popen(cmd//c_null_char, 'r'//c_null_char) | ||
|
||
if (.not. c_associated(handle)) then | ||
if (present(has_error)) has_error = .true.; return | ||
end if | ||
|
||
do while (c_associated(fgets(line, buffer_length, handle))) | ||
str = str//c2f(line) | ||
end do | ||
|
||
istat = pclose(handle) | ||
if (present(has_error)) has_error = istat /= 0 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
program run_f_test | ||
use run_f, only: run | ||
implicit none | ||
|
||
character(:), allocatable :: str | ||
logical :: has_error | ||
|
||
str = run('') | ||
if (str /= '') call fail('Empty command has output (no error handling).') | ||
|
||
str = run('', has_error) | ||
if (str /= '') call fail('Empty command has output.') | ||
if (has_error) call fail('Empty command has error.') | ||
|
||
str = run('whoami') | ||
if (str == '') call fail('whoami has no output (no error handling).') | ||
|
||
str = run('whoami', has_error) | ||
if (str == '') call fail('whoami has no output.') | ||
if (has_error) call fail('whoami failed.') | ||
|
||
str = run('whoami', has_error, print_cmd=.false.) | ||
if (str == '') call fail('whoami has no output (no print).') | ||
if (has_error) call fail('whoami failed.') | ||
|
||
str = run('whoami', has_error, print_cmd=.true.) | ||
if (str == '') call fail('whoami has no output (with print).') | ||
if (has_error) call fail('whoami failed.') | ||
|
||
str = run('whoami -x', has_error) | ||
if (.not. has_error) call fail('whoami with invalid option did not fail.') | ||
|
||
str = run('whoami xyz', has_error) | ||
if (.not. has_error) call fail('whoami with invalid argument did not fail.') | ||
|
||
str = run('abcdefg', has_error) | ||
if (.not. has_error) call fail('Invalid command did not fail.') | ||
|
||
print *, achar(10)//achar(27)//'[92m All tests passed.'//achar(27) | ||
|
||
contains | ||
|
||
subroutine fail(msg) | ||
character(*), intent(in) :: msg | ||
print *, achar(27)//'[31m'//'Test failed: '//msg//achar(27)//'[0m' | ||
stop 1 | ||
end | ||
end |