Adicionando dependencias
Contenido
Adicionando dependencias¶
Este tutorial cubre el uso de dependencias con fpm y cómo reutilizar proyectos fpm existentes.
Usando la biblioteca estándar¶
Iniciamos un nuevo proyecto con fpm, deseamos construir un programa de línea de comandos que lee un archivo, encuentre cierto patrón y lo reemplaze. Ya que no queremos escribir una función para reemplazar, usamos la biblioteca estándar de Fortran (stdlib) como dependencia. En el manifiesto del paquete definimos stdlib en la tabla de dependencias:
name = "demo"
version = "0.1.0"
[dependencies]
stdlib = "*"
Ahora creamos un módulo con un procedimiento para realizar la substitución. Esto requiere tres pasos:
leer una línea completa desde la unidad uno
reemplazar el patrón en la cadena de caracteres
escribir la nueva cadena de caracteres en un archivo de salida
Para esto, usaremos la función replace_all desde el módulo stdlib_strings. La implementación es mostrada aquí
module demo
use stdlib_io, only : getline
use stdlib_strings, only : replace_all
implicit none
private
public :: substitute
contains
!> Read all lines from input, replace pattern and print it to output
subroutine substitute(input, output, pattern, replacement)
!> Formatted input unit
integer, intent(in) :: input
!> Formatted output unit
integer, intent(in) :: output
!> Pattern to replace in input
character(len=*), intent(in) :: pattern
!> Replacement for pattern in output
character(len=*), intent(in) :: replacement
character(len=:), allocatable :: line
integer :: stat
do
call getline(input, line, stat)
if (stat /= 0) exit
write(output, '(a)') replace_all(line, pattern, replacement)
end do
end subroutine substitute
end module demo
Finalmente, necesitamos un controlador de línea de comandos para usar la nueva función.
program main
use, intrinsic :: iso_fortran_env, only : output_unit
use demo, only : substitute
implicit none
character(len=256) :: pattern, replacement, input_file
integer :: input
call get_command_argument(1, pattern)
call get_command_argument(2, replacement)
call get_command_argument(3, input_file)
open(newunit=input, file=input_file, status='old')
call substitute(input, output_unit, trim(pattern), trim(replacement))
close(input)
end program main
Podemos verificar el controlador de línea de comandos ejecutando con fpm:
❯ fpm run -- demo substitute fpm.toml
name = "substitute"
version = "0.1.0"
[dependencies]
stdlib = "*"
Adicionando un marco de pruebas¶
Antes de adicionar nuevas características, deseamos realizar algunas pruebas para verificar si nuestra implementación sigue funcionando a medida que esta es modificada. Un marco de pruebas minimalista está disponible con [test-drive]. Dado que el marco de pruebas es solo requerido durante el desarrollo del paquete, pero no para otros paquetes que puedan ser usados en el futuro para nuestros módulos, adicionamos una dependencia local. El paquete test-drive es adicionado en la table dev-dependencies como se muestra abajo
name = "demo"
version = "0.1.0"
[dependencies]
stdlib = "*"
[dev-dependencies]
test-drive.git = "https://github.com/fortran-lang/test-drive"
test-drive.tag = "v0.4.0"
Nota
Para una dependencia de desarrollo similar a un marco de prueba, elegimos un estricto pin de la versión especificando la tag que queremos usar.
Ahora podemos escribir una prueba unitária simple, dado que nuestra función trabaja con unidades, usaremos unidades borrador para definir las entradas y las salidas. Por ahora, adicionaremos una única substitución de línea como caso de prueba
module test_demo
use demo, only : substitute
use stdlib_io, only : getline
use testdrive, only : error_type, unittest_type, new_unittest, check
implicit none
private
public :: collect_demo
contains
!> Collect all exported unit tests
subroutine collect_demo(testsuite)
!> Collection of tests
type(unittest_type), allocatable, intent(out) :: testsuite(:)
testsuite = [new_unittest("substitute", test_substitute)]
end subroutine collect_demo
!> Check substitution of a single line
subroutine test_substitute(error)
!> Error handling
type(error_type), allocatable, intent(out) :: error
integer :: input, output, stat
character(len=:), allocatable :: line
open(newunit=input, status="scratch")
write(input, '(a)') "This is a valid test"
rewind(input)
open(newunit=output, status="scratch")
call substitute(input, output, "test", "example")
close(input)
rewind(output)
call getline(output, line, stat)
close(output)
call check(error, line, "This is a valid example")
end subroutine test_substitute
end module test_demo
program tester
use, intrinsic :: iso_fortran_env, only : error_unit
use testdrive, only : run_testsuite
use test_demo, only : collect_demo
implicit none
integer :: stat
stat = 0
call run_testsuite(collect_demo, error_unit, stat)
if (stat > 0) then
write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!"
error stop
end if
end program tester
Ejecutamos nuestra nueva prueba usando fpm
❯ fpm test
Starting substitute ... (1/1)
... substitute [PASSED]
La creación de unidades borrador para múltiples pruebas unitárias será repetitiva, este tipo de tareas normalmente se pueden realizar en un procedimiento separado y reutilizarse en varias pruebas.
Dependencias de objetivo específico¶
Las dependencias también se pueden ser usadas, solamente, para objetivos específicos. Esto se puede usar para adicionar un paquete de interfaz de línea de comandos, que solo se utiliza para el ejecutable pero que no forma parte de las dependencias de la biblioteca.
name = "demo"
version = "0.1.0"
[dependencies]
stdlib = "*"
[dev-dependencies]
test-drive.git = "https://github.com/fortran-lang/test-drive"
test-drive.tag = "v0.4.0"
[[executable]]
name = "demo"
[executable.dependencies]
M_CLI2.git = "https://github.com/urbanjost/M_CLI2"
Reestructuramos un poco nuestro programa principal para usar [M \ _ CLI2] para manejar la entrada de línea de comandos. La matriz unname contiene todos los comandos posicionales de argumentos de línea, todavía usamos los dos primeros como patrón y reemplazo, y usamos todos los argumentos restantes como entrada. También agregamos una opción a redirigir la salida. Nuestro programa principal final parece
program main
use, intrinsic :: iso_fortran_env, only : output_unit
use demo, only : substitute
use m_cli2, only : set_args, unnamed, sget
implicit none
character(len=:), allocatable :: input_file, output_file, pattern, replacement
integer :: input, output, i
call set_args("--output:o ''")
output_file = trim(sget("output"))
if (len(output_file) > 0) then
open(file=output_file, newunit=output)
else
output = output_unit
end if
pattern = trim(unnamed(1))
replacement = trim(unnamed(2))
do i = 3, size(unnamed)
input_file = trim(unnamed(i))
open(file=input_file, newunit=input, status='old')
call substitute(input, output_unit, trim(pattern), trim(replacement))
close(input)
end do
if (output /= output_unit) close(output)
end program main
Nuevamente ejecutamos y rápidamente verificamos con fpm
❯ fpm run -- demo substitute fpm.toml
name = "substitute"
version = "0.1.0"
[dependencies]
stdlib = "*"
[dev-dependencies]
test-drive.git = "https://github.com/fortran-lang/test-drive"
test-drive.tag = "v0.4.0"
[[executable]]
name = "substitute"
[executable.dependencies]
M_CLI2.git = "https://github.com/urbanjost/M_CLI2"
La salida luce como se espera con dos substituciones.
Resumen
En este tutorial aprenderas cómo
depender de otro proyecto fpm en el manifiesto del paquete
añadir dependencias de desarrollo para pruebas
usar dependencias para ejecutables