fgof-process Public
Code
Use Git or checkout with SVN using the web URL.
No matching headings.
fgof-process
POSIX-first process and subprocess helpers for modern Fortran applications.
fgof-process is intended to be a small, standalone library that gives Fortran tools a more ergonomic process API than raw execute_command_line, thin POSIX wrappers, or experimental process surfaces that still feel too low-level for real tooling.
It is built for the kind of Fortran programs that need to orchestrate other tools: shells, editors, TUI apps, developer tooling, test fixtures, and automation helpers.
It is the first package in the FortranGoingOnForty lib-modules catalog, but it is intended to stand on its own as a normal fpm package.
Current v1 target:
- argv-first command construction
- synchronous process execution on macOS and Linux
- explicit shell-command convenience through
/bin/sh -c - environment overrides
- working-directory overrides
- stdin support
- stdout and stderr capture
- exit-status reporting
- timeout-aware execution
- structured, result-first errors
Future scope:
- streaming process handles
- async spawn and wait
- signal helpers
- PTY-friendly integration points for a future
fgof-pty - a dedicated
fgof-proc-testcompanion package
Status
Sync-first v0.1.1 is released.
The core synchronous path is implemented and tested. v0.1.1 carries the
post-v0.1.0 C interface binding wrap needed by downstream CI compilers.
Implemented today:
- public
fgof_processandfgof_process_typesmodules command()andshell()constructors- synchronous argv and shell execution
- child-only cwd override
- child-only env set and unset behavior
- stdin piping
- stdout capture
- stderr capture
- timeout enforcement with partial output preservation
- result-first error codes for invalid commands, spawn failures, exec failures, and pipe setup failures
Still deferred:
- async process handles
- streaming process handles
Why Use It
- argv-first execution is the default, so spaces and shell metacharacters stay literal unless you explicitly choose
shell() - cwd and environment overrides apply only to the child process
- stdout, stderr, stdin, exit status, and timeout handling live in one small API
- nonzero child exits are reported as process outcomes, not library failures
- result values are structured enough for test code and tool code to branch on cleanly
Package Goals
- keep the API small and predictable
- prefer argv-based execution over shell-string execution
- make tests easy to write
- stay useful for shells, editors, TUI apps, and developer tooling
- close a real ecosystem gap rather than mirroring
stdlib_system
Public API Shape
Primary module:
fgof_process
Public types:
process_commandprocess_optionsprocess_result
Public procedures:
commandshellrun
Quick Start
program demo_run
use fgof_process, only : FGOF_PROCESS_OK, command, process_options, process_result, run
implicit none
character(len=6) :: argv(2)
type(process_options) :: opts
type(process_result) :: res
argv = [character(len=6) :: "%s", "hello"]
opts%capture_stdout = .true.
res = run(command("printf", argv), opts)
if (res%error_code /= FGOF_PROCESS_OK) error stop trim(res%error_message)
if (res%exit_code /= 0) error stop "child command failed"
print "(A)", res%stdout
end program demo_run
Build And Test
fpm test
That is the baseline verification command locally and in CI.
command() vs shell()
Choose command() when you already know the executable and argument list.
res = run(command("git", [character(len=7) :: "status", "--short"]), opts)
This path does not interpret pipes, redirects, semicolons, or $VARS. They are passed through as literal argv values.
Choose shell() only when you need shell features such as pipelines or redirection.
res = run(shell("printf 'hello' | sed 's/hello/world/'"), opts)
This path uses /bin/sh -c on POSIX systems.
Common Patterns
Capture stdout:
opts = process_options()
opts%capture_stdout = .true.
res = run(command("pwd"), opts)
Capture stderr:
opts = process_options()
opts%capture_stderr = .true.
res = run(shell("printf 'oops' >&2"), opts)
Pipe stdin into a child:
opts = process_options()
opts%stdin = "hello" // new_line("a")
res = run(shell("read value; test ""$value"" = ""hello"""), opts)
Run in a different working directory:
opts = process_options()
opts%cwd = "/tmp"
res = run(command("pwd"), opts)
Set or unset child-only environment variables:
opts = process_options()
opts%env_set = ["FGOF_MODE=test"]
opts%env_unset = ["OLD_ENV_FLAG"]
res = run(shell("env"), opts)
Enforce a timeout and keep partial output:
opts = process_options()
opts%capture_stdout = .true.
opts%timeout_ms = 100
res = run(shell("printf 'begin'; sleep 2"), opts)
Result Semantics
run() returns a process_result with three kinds of information:
- launch and completion state:
launched,completed,timed_out,exited_normally - child outcome:
exit_code,term_signal, capturedstdout, capturedstderr - library outcome:
error_code,error_message,elapsed_ms
Important rule:
- a nonzero child
exit_codeis not a library error - invalid input, pipe setup failure, spawn failure, exec failure, and timeout are library errors surfaced through
error_code
Supported Platforms
- macOS
- Linux
- GitHub Actions CI validates the package on
macos-latestandubuntu-latestwith the GCC toolchain andfpm v0.13.0
Current Boundaries
- v1 uses a direct POSIX backend on macOS and Linux.
stdlib_systemcan inform naming and behavior, but is not a required dependency.- Async process handles are explicitly deferred until after the sync-first release.
Development Notes
- POSIX first: macOS and Linux
- standalone
fpmpackage - intended to remain independently versioned and releasable
License
MIT