Abhängigkeiten hinzufügen
Inhalt
Abhängigkeiten hinzufügen¶
Dieses Tutorial behandelt die Benutzung von Abhängigkeiten mit fpm und wie ein existierendes fpm-Projekt wiederverwenden werden kann.
Benutzung der Standardbibliothek¶
Wir beginnen mit einem neuen Projekt mit fpm, wir möchten eine Kommandozeilenanwendung bauen, die eine Datei liest, einebestimmtes Suchmuster findet und ersetzt. Da wir nicht die Funktion zum Ersetzen selbst schreiben wollen, werden wir die Standardbibliothek (stdlib) als Abhängigkeit benutzen. Im Paketmanifest definieren wir stdlib in der depedencies Tabelle:
name = "demo"
version = "0.1.0"
[dependencies]
stdlib = "*"
Zuerst müssen wir ein Modul mit einer Prozedur erstellen, das die Substitution durchführt. Diese benötigt drei Schritte:
eine ganze Zeile von einer Adresse lesen
ein Muster in einem String ersetzen
einen neuen String in der Ausgabe schreiben
Wir werden die replace_all-Funktion aus dem stdlib_strings-Modul für diesen Zweck verwenden. Die Implementierung ist hier angegeben
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
Zuletzt brauchen wir einen Kommandozeilen-Treiber, um unsere neue Funktion zu nutzen.
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
Wir können unseren Kommandozeilen-Treiber über fpm testen:
❯ fpm run -- demo substitute fpm.toml
name = "substitute"
version = "0.1.0"
[dependencies]
stdlib = "*"
Hinzufügen eines Test-Frameworks¶
Bevor wir weitermachen, um neue Funktionen zu implementieren, möchten wir einige Tests hinzufügen, um zu verifizieren, dass unsere Implementierung weiterhin funktioniert, wenn wir es ändern. Ein minimalistes Test-Framework ist verfügbar mit [test-drive]. Da das Test-Framework nur benötigt wird, wenn wir das Paket selbst entwickeln, aber nicht für andere Pakete, die in der Zukunft unsere Module nutzen, fügen wir eine lokale Abhängigkeit hinzu. Das test-drive-Paket wird in der dev-dependencies-Tabelle wie unten angegeben hinzugefügt
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"
Bemerkung
Für eine Entwicklungsabhängigkeit wie das Test-Framework wählen wir eine strikte Versions Einschränkung, indem wir den tag angeben, den wir benutzen wollen.
Wir können nun einen einfachen Test schreiben, da unsere Funktion mit Units arbeitet, werden wir temporäre Units erstellen, um den Eingang und die Ausgabe zu erfassen. Für den Moment werden wir eine einfache Ersetzung in einer Zeile als Test hinzufügen
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
Wir führen unseren neuen Test mit fpm aus
❯ fpm test
Starting substitute ... (1/1)
... substitute [PASSED]
Die Erstellung der temporären Units für mehrere Unit-Tests wird repetitiv sein, diese Aufgaben können in einer separatem Prozedur erledigt werden und in mehreren Tests wiederverwendet werden.
Ziel-spezifische Abhängigkeiten¶
Abhängigkeiten können auch nur für bestimmte Ziele verwendet werden. Dies kann verwendet werden, um ein Kommandozeilen-Interface-Paket hinzuzufügen, das nur für die Ausführungsdatei benutzt wird, aber nicht Teil der Bibliotheksabhängigkeiten ist.
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"
Wir restrukturieren unsere Hauptprogramm etwas, um [M_CLI2] zu benutzen was die Kommandozeileneingabe verarbeitet. Das unnamed-Feld enthält alle positionsbezogenen Kommandozeilenargumente, wir benutzen die ersten zwei als Muster und Ersetzung, und verwenden alle weiteren Argumente als Eingabe. Wir fügen auch eine Option hinzu, um die Ausgabe zu umleiten. Unser finales Hauptprogramm sieht wie folgt aus
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
Wir führen einen kurzen Test mit fpm aus
❯ 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"
Die Ausgabe sieht wie erwartet aus, mit zwei Ersetzungen.
Zusammenfassung
In diesem Kurs haben wir gelernt, wie man
von einem anderen fpm-Projekt im Paketmanifest abhängen
Hinzufügen von Entwicklungsabhängigkeiten zum Testen
Verwenden von Abhängigkeiten für ausführbare Dateien