Cargo Çalışma Alanları
- bölümde hem ikili crate hem de kütüphane crate içeren bir paket oluşturmuştuk. Projeniz büyüdükçe kütüphane crate’in de büyümeye devam ettiğini ve paketi birden fazla kütüphane crate’e bölmek istediğinizi fark edebilirsiniz. Cargo’nun çalışma alanı (workspace) adı verilen özelliği, birlikte geliştirilen ilişkili birden çok paketi yönetmeyi kolaylaştırır.
Bir Çalışma Alanı Oluşturmak
Çalışma alanı, aynı Cargo.lock dosyasını ve aynı çıktı dizinini paylaşan
paketler kümesidir. Yapıya odaklanabilmek için basit kod kullanan bir çalışma
alanı örneği oluşturalım. Çalışma alanını düzenlemenin farklı yolları vardır;
biz yalnızca yaygın bir yaklaşımı göstereceğiz. Bir ikili crate ve iki kütüphane
crate içeren bir çalışma alanımız olacak. Ana işlevi sunacak ikili crate,
iki kütüphaneye bağımlı olacak. Kütüphanelerden biri bir_ekle fonksiyonunu,
diğeri ise iki_ekle fonksiyonunu sağlayacak. Bu üç crate aynı çalışma alanının
parçası olacak. Önce çalışma alanı için yeni bir dizin oluşturalım:
$ mkdir add
$ cd add
Ardından add dizininde, tüm çalışma alanını yapılandıracak Cargo.toml
dosyasını oluşturalım. Bu dosyada [package] bölümü olmayacak. Onun yerine
üyeleri eklememizi sağlayacak bir [workspace] bölümüyle başlayacak. Ayrıca
çözümleyicinin en güncel sürümünü kullanmak için resolver değerini "3"
yapacağız:
Dosya Adı: Cargo.toml
[workspace]
resolver = "3"
Şimdi topla dizini içinde cargo new çalıştırarak toplayici ikili crate’ini
oluşturalım:
$ cargo new toplayici
Created binary (application) `toplayici` package
Adding `toplayici` as member of workspace at `file:///projects/topla`
Bir çalışma alanı içinde cargo new çalıştırıldığında, Cargo yeni oluşturulan
paketi çalışma alanı Cargo.toml dosyasındaki [workspace] tanımının members
anahtarına da otomatik olarak ekler:
[workspace]
resolver = "3"
members = ["toplayici"]
Bu noktada cargo build çalıştırarak çalışma alanını derleyebiliriz. topla
dizininizin dosya yapısı şu hale gelir:
├── Cargo.lock
├── Cargo.toml
├── toplayici
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
Çalışma alanının en üst düzeyde tek bir target dizini vardır ve derlenmiş
yapıtlar bu dizine yazılır; toplayici paketinin kendine ait bir target dizini
yoktur. Hatta cargo buildi toplayici dizininden çalıştırsak bile çıktılar yine
topla/target altına gider; topla/toplayici/target altına değil. Cargo çalışma
alanlarında target dizinini böyle düzenler çünkü bu crate’lerin birbirine
bağımlı olması beklenir. Her crate’in kendi target dizini olsaydı, her crate
diğer crate’leri de kendi dizinine çıktı koymak için yeniden derlemek zorunda
kalırdı. Ortak bir target dizini paylaşmak gereksiz yeniden derlemeleri önler.
Çalışma Alanına İkinci Paketi Eklemek
Şimdi çalışma alanına bir üye paket daha ekleyelim ve buna bir_ekle diyelim.
bir_ekle adlı yeni bir kütüphane crate’i üretin:
$ cargo new bir_ekle --lib
Created library `bir_ekle` package
Adding `bir_ekle` as member of workspace at `file:///projects/topla`
Üst düzey Cargo.toml artık members listesinde bir_ekle yolunu da içerir:
Dosya Adı: Cargo.toml
[workspace]
resolver = "3"
members = ["toplayici", "bir_ekle"]
topla dizininizin dosya yapısı artık şöyle olur:
├── Cargo.lock
├── Cargo.toml
├── bir_ekle
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── toplayici
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
Şimdi bir_ekle/src/lib.rs dosyasına bir_ekle fonksiyonunu ekleyelim:
Dosya Adı: bir_ekle/src/lib.rs
pub fn bir_ekle(x: i32) -> i32 {
x + 1
}
Artık ikili crate’imizi barındıran toplayici paketinin, bu kütüphaneyi içeren
pakete bağımlı olmasını sağlayabiliriz. Önce toplayici/Cargo.toml dosyasına bu
crate için yol bağımlılığı eklememiz gerekir.
Dosya Adı: toplayici/Cargo.toml
[dependencies]
bir_ekle = { path = "../bir_ekle", package = "bir_ekle" }
Cargo, çalışma alanındaki crate’lerin birbirine bağımlı olacağını kendiliğinden varsaymaz; bu ilişkileri açıkça yazmamız gerekir.
Şimdi toplayici crate’inde bir_ekle fonksiyonunu kullanalım. toplayici/src/main.rs
dosyasını açıp main fonksiyonunu, Liste 14-7’deki gibi bir_ekle
fonksiyonunu çağıracak şekilde değiştirin.
fn main() {
let sayi = 10;
println!(
"Merhaba, dünya! {sayi} artı bir {} eder!",
bir_ekle::bir_ekle(sayi)
);
}
toplayici crate’inden bir_ekle kutuphane fonksiyonunu kullanmakŞimdi çalışma alanının en üst düzeyindeki topla dizininde cargo build
çalıştırarak her şeyi derleyelim!
$ cargo build
Compiling bir_ekle v0.1.0 (file:///projects/topla/bir_ekle)
Compiling toplayici v0.1.0 (file:///projects/topla/toplayici)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
İkili crate’i topla dizininden çalıştırmak için, çalışma alanındaki hangi paketi
çalıştırmak istediğimizi -p bayrağı ve paket adıyla belirtiriz:
$ cargo run -p toplayici
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/toplayici`
Merhaba, dünya! 10 artı bir 11 eder!
Bu komut, toplayici/src/main.rs içindeki ve bir_ekle crate’ine bağımlı olan kodu
çalıştırır.
Harici Bir Pakete Bağımlı Olmak
Çalışma alanında her crate dizininde ayrı bir Cargo.lock yerine, üst düzeyde
tek bir Cargo.lock dosyası bulunduğuna dikkat edin. Bu, tüm crate’lerin tüm
bağımlılıkların aynı sürümünü kullanmasını sağlar. toplayici/Cargo.toml ve
bir_ekle/Cargo.toml dosyalarına rand paketini eklersek, Cargo bunları tek
bir rand sürümüne çözer ve bunu tek Cargo.lock dosyasına yazar. Böylece
çalışma alanındaki tüm crate’ler birbirleriyle uyumlu kalır. Şimdi rand
crate’ini bir_ekle/Cargo.toml içindeki [dependencies] bölümüne ekleyelim ki
onu bir_ekle crate’inde kullanabilelim:
Dosya Adı: bir_ekle/Cargo.toml
[dependencies]
rand = "0.8.5"
Şimdi bir_ekle/src/lib.rs dosyasına use rand; ekleyebiliriz. Ardından topla
dizininde cargo build çalıştırınca, tüm çalışma alanıyla birlikte rand
crate’i de indirilip derlenir. randı kapsama aldığımız halde kullanmadığımız
için bir uyarı görürüz:
$ cargo build
Updating crates.io index
Downloaded rand v0.8.5
--snip--
Compiling rand v0.8.5
Compiling bir_ekle v0.1.0 (file:///projects/topla/bir_ekle)
warning: unused import: `rand`
--> bir_ekle/src/lib.rs:1:5
|
1 | use rand;
| ^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: `bir_ekle` (lib) generated 1 warning (run `cargo fix --lib -p bir_ekle` to apply 1 suggestion)
Compiling toplayici v0.1.0 (file:///projects/topla/toplayici)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s
Artık üst düzey Cargo.lock dosyası, bir_ekle crate’inin rand bağımlılığına
ilişkin bilgiyi içerir. Ama rand çalışma alanında bir yerde kullanılıyor diye,
onu diğer crate’lerde de otomatik kullanamayız; bunun için onların Cargo.toml
dosyalarına da rand eklememiz gerekir. Örneğin toplayici paketine ait
toplayici/src/main.rs dosyasına use rand; yazarsak hata alırız:
$ cargo build
--snip--
Compiling toplayici v0.1.0 (file:///projects/topla/toplayici)
error[E0432]: unresolved import `rand`
--> toplayici/src/main.rs:2:5
|
2 | use rand;
| ^^^^ no external crate `rand`
Bunu düzeltmek için toplayici paketinin Cargo.toml dosyasını düzenleyip randın
ona da bağımlılık olduğunu belirtmemiz gerekir. toplayici paketini derlemek,
Cargo.lock içinde toplayici için bağımlılık listesine randı ekler; ama randın
ek bir kopyası indirilmez. Cargo, çalışma alanındaki her pakette rand
kullanan crate’lerin, uyumlu sürüm belirttikleri sürece aynı rand sürümünü
kullanmasını sağlar. Bu da hem yer kazandırır hem de crate’lerin birbiriyle
uyumlu kalmasını sağlar.
Çalışma alanındaki crate’ler aynı bağımlılığın birbiriyle uyumsuz sürümlerini isterse, Cargo hepsini ayrı ayrı çözer; ama yine de olabildiğince az sayıda sürüm kullanmaya çalışır.
Çalışma Alanına Test Eklemek
Bir geliştirme daha yapalım ve bir_ekle crate’i içinde bir_ekle::bir_ekle
fonksiyonu için bir test ekleyelim:
Dosya Adı: bir_ekle/src/lib.rs
pub fn bir_ekle(x: i32) -> i32 {
x + 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn calisir() {
assert_eq!(3, bir_ekle(2));
}
}
Şimdi üst düzey topla dizininde cargo test çalıştırın. Böyle yapılandırılmış
bir çalışma alanında cargo test, çalışma alanındaki tüm crate’lerin testlerini
çalıştırır:
$ cargo test
Compiling bir_ekle v0.1.0 (file:///projects/topla/bir_ekle)
Compiling toplayici v0.1.0 (file:///projects/topla/toplayici)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s
Running unittests src/lib.rs (target/debug/deps/bir_ekle-93c49ee75dc46543)
running 1 test
test tests::calisir ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/toplayici-3a47283c568d2b6a)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests bir_ekle
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Çıktının ilk bölümü, bir_ekle crate’i içindeki calisir testinin geçtiğini
gösterir. Sonraki bölümde toplayici crate’inde hiç test bulunmadığını, son bölümde
de bir_ekle crate’inde hiç belge testi olmadığını görürüz.
Üst düzey dizinden, çalışma alanındaki tek bir crate için de test
çalıştırabiliriz. Bunun için -p bayrağını kullanıp test etmek istediğimiz
crate adını belirtiriz:
$ cargo test -p bir_ekle
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.00s
Running unittests src/lib.rs (target/debug/deps/bir_ekle-93c49ee75dc46543)
running 1 test
test tests::calisir ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests bir_ekle
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Bu çıktı, cargo testin yalnızca bir_ekle crate’inin testlerini
çalıştırdığını ve toplayici crate’inin testlerini çalıştırmadığını gösterir.
Çalışma alanındaki crate’leri crates.io
üzerinde yayımlarsanız, her crate’i ayrı ayrı yayımlamanız gerekir. cargo test
örneğinde olduğu gibi, -p bayrağını kullanarak çalışma alanındaki belirli bir
crate’i yayımlayabilirsiniz.
Ek pratik olarak, bu çalışma alanına iki_ekle adında bir crate’i de bir_ekle
crate’iyle benzer biçimde ekleyin!
Projeniz büyüdükçe çalışma alanı kullanmayı düşünün: Tek parça büyük bir kod yığını yerine daha küçük ve daha kolay anlaşılır bileşenlerle çalışmanıza olanak tanır. Üstelik çalışma alanındaki crate’ler aynı anda sık değişiyorsa, aralarındaki eşgüdümü de kolaylaştırır.