Adicionando dependências
Conteúdo
Adicionando dependências¶
Este tutorial cobre o uso de dependências com o fpm e como reutilizar projetos fpm já existentes.
Usando a biblioteca padrão¶
Vamos começar com um novo projeto, queremos construir uma aplicação de linha de comando que lê um ficheiro, encontra um padrão e o substitui. Dado que não queremos escrever a função de substituição nós mesmos, vamos usar a Biblioteca Padrão do Fortran (stdlib) como dependência. No manifesto do pacote vamos definir stdlib na tabela de dependencies:
name = "demo"
version = "0.1.0"
[dependencies]
stdlib = "*"
Agora, criaremos o modulo com um procedimento para realizar a substituição. Isso requer três passos:
ler a linha inteira de uma unidade
substituir o padrão na cadeia
escrever a nova cadeia numa saída
Usaremos a função replace_all do módulo stdlib_strings para este propósito. A implementação é mostrada a seguir
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
Por fim, precisamos de um programa principal para usar nossa nova função.
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 conferir nosso programa rodando-o com o fpm:
❯ fpm run -- demo substitute fpm.toml
name = "substitute"
version = "0.1.0"
[dependencies]
stdlib = "*"
Adicionando um framework de testes¶
Antes que continuemos a implementar novos recursos, queremos adicionar alguns testes para verificar que nossa implementação continua funcionando conforme a modificamos. Um framework minimalista de testes está disponível em test-drive. Já que o framework de testes é necessário apenas ao desenvolvermos o pacote em si, mas não para outros pacotes que por ventura usem nossos módulos, adicionaremos uma dependência de desenvolvimento. O pacote test-drive é adicionado na tabela dev-dependencies como é mostrado abaixo
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 dependências de desenvolvimento como um framework de testes vamos escolher uma versão fixa através da tag que queremos usar.
Agora podemos escrever um simples teste unitário, já que nossa função funciona com uma unit, vamos criar units do tipo scratch para criar a entrada e capturar a saída. Por enquanto, vamos adicionar uma simples substituição em uma linha como um caso de teste
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
Rodamos o nosso novo teste usando o fpm
❯ fpm test
Starting substitute ... (1/1)
... substitute [PASSED]
Criar unidades do tipo scratch para múltiplos teste de unidade pode ser uma tarefa repetitiva, este tipo de coisa pode ser feita em um procedimento a parte e reutilizada em vários testes.
Dependências específicas do alvo¶
Dependências também podem ser usadas apenas para alvos específicos. Isso pode ser feito para adicionar uma interface de linha de comando que é usada no executável mas não é parte das dependências da 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"
Reestruturamos nosso programa principal um pouco para usar o M_CLI para lidar com a interface de linha de comando. O array unnamed contém todos os argumentos posicionais do comando, vamos usar os dois primeiros como padrão e seu substituto e usar o todos os restantes como entrada. Além disso, adicionamos a opção de redirecionar a saída. Ao final, nosso programa ficará assim
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
Mais uma vez fazemos uma verificação rápida usando o 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"
A saída está conforme o esperado, com duas substituições.
Sumário
Neste tutorial você aprendeu como
depender de outro projeto fpm no manifesto do pacote
adicionar dependências de desenvolvimento para testes
usar dependências para os executáveis