Ders 07 / 12
Geliştirme Ortamı
Proje, hedef ve çalıştırma şeması kavramlarında aynı derleme ayarının iki düzeyde tanımlanmasının, arama yollarının genişletilmesinin ve sürüm aralıklarının buluşmasının ölçümü.
Yayın Güncelleme
Bu derste
- Proje ve hedef düzeyinde aynı derleme ayarı tanımlandığında hangi değerin geçerli olduğunu ölçer
- Arama yollarındaki devralma işaretinin öncelik ile birleşim arasındaki farkı nasıl belirlediğini gösterir
- Asgari sürümün tek değerli bir ayar olarak hedefte ezildiğini, uygulamanın desteklediği sürümlerle çalıştırma hedeflerinin sürümlerinin ise kesiştiğini ölçer
- Çalıştırma şemasının bir tanım değil bir seçim olduğunu ve buluşma doğurmadığını açıklar
İçindekiler
Önceki konunun son dersi iki tanımı yan yana koydu: uygulamanın desteklediği asgari sürüm bir kümeyi, platform arayüzünün sürümü bir başka kümeyi tanımlıyordu ve geçerli olan, ikisinin kesişimiydi. Bu ders aynı soruyu geliştirme ortamının kendi katmanlarına taşır. Bir uygulamanın derlenmesi birden çok yerde yazılmış ayara dayanır: tek bir proje (project), projenin altında bir ya da birkaç hedef (target) ve bunları belirli bir amaçla çalıştıran çalıştırma şeması (scheme). Aynı ayar hem projede hem hedefte yazılabilir. İkisi çelişirse hangisi geçerlidir, ikisi birlikte mi geçerlidir, yoksa derleme hiç başlamaz mı?
Kursun ölçü ekseni bu sorudur: iki tanım buluşunca nasıl çözülüyor? Dört çözüm vardır. Tanımlardan biri ötekini ezerse çözüm öncelik, yalnız ortak kısım kalırsa kesişim, ikisi birleşirse birleşim, ikisi birlikte kabul edilemezse ret. Kursun açılış dersinde sekiz buluşmayı tek bir sınıflandırıcıyla ölçmüş, dağılımı 2/2/2/2 bulmuştuk. Bu ders o sınıflandırıcıyı değiştirmeden kullanır; yalnız bu dersin kendi buluşmalarını ölçer. Sınıflandırıcı kuralı sayar, niyeti okumaz: iki tanımın biri ötekinin alt kümesiyse ya da biri boşsa ortada bir buluşma yoktur ve ölçüm dışıdır.
Sınıflandırıcı
Meeting iki tanımı küme olarak tutar, çözümün sonucunu üçüncü bir küme olarak alır ve sonucu iki tanıma bakarak adlandırır. Sonuç elle yazılmaz; her ölçümde dersin kurduğu küçük bir modelden gelir. Çıktı satırı hep aynı biçimdedir: etiket, ilk tanım, ikinci tanım, sonuç ve çözüm.
// Meeting.swift — iki tanim bulusunca nasil cozuldugunu siniflandirir
struct Meeting {
let label: String
let first: Set<String>
let second: Set<String>
let result: Set<String>?
init(_ label: String, first: Set<String>, second: Set<String>, result: Set<String>?) {
self.label = label
self.first = first
self.second = second
self.result = result
}
var resolution: String {
if first.isEmpty || second.isEmpty
|| first.isSubset(of: second) || second.isSubset(of: first) {
return "olcum-disi"
}
guard let result, !result.isEmpty else { return "ret" }
if result == first.union(second) { return "birlesim" }
if result == first.intersection(second) { return "kesisim" }
if result == first || result == second { return "oncelik" }
return "kural-disi"
}
static func list(_ values: Set<String>) -> String {
values.isEmpty ? "bos" : values.sorted().joined(separator: ",")
}
func report() {
let outcome = result.map(Meeting.list) ?? "yok"
print("\(label): ilk \(Meeting.list(first)) | ikinci \(Meeting.list(second)) | sonuc \(outcome) -> \(resolution)")
}
}
func summarize(_ meetings: [Meeting]) {
for resolution in ["oncelik", "kesisim", "birlesim", "ret"] {
let count = meetings.filter { $0.resolution == resolution }.count
print("\(resolution): \(count)")
}
}
Aynı Ayar İki Düzeyde
Derleme ayarı (build setting), derleyiciye ve bağlayıcıya ne yapacağını söyleyen bir anahtar ve değer çiftidir: en iyileme düzeyi, asgari desteklenen sürüm, uyarıların açık ya da kapalı olması. Proje bu çiftleri tüm hedefler için varsayılan olarak tanımlar. Her hedef kendi derleme ayarlarını da taşır. Bir anahtar iki düzeyde de yazılmışsa platformun kuralı bellidir: hedefin değeri projeninkini ezer. Bir anahtar yalnız projede yazılmışsa hedef onu olduğu gibi devralır.
Bu davranışı küçük bir modelle kurarız. Model iki sözlüğü birleştirir ve çakışan anahtarlarda hedefin değerini tutar. Üç anahtar seçtik: iki düzeyde farklı değerlerle yazılan en iyileme düzeyi, iki düzeyde farklı değerlerle yazılan asgari sürüm ve yalnız projede yazılan uyarı düzeyi. Her anahtar için bir buluşma kurulur: ilk tanım projenin değeri, ikinci tanım hedefin değeri, sonuç modelin geçerli saydığı değerdir.
// main.swift — ayni anahtar iki duzeyde tanimliysa hedef duzeyi kazanir
func resolveSettings(project: [String: String], target: [String: String]) -> [String: String] {
project.merging(target) { _, targetValue in targetValue }
}
func single(_ value: String?) -> Set<String> {
value.map { [$0] } ?? []
}
func settingMeeting(_ key: String, project: [String: String], target: [String: String]) -> Meeting {
let effectiveValues = resolveSettings(project: project, target: target)
return Meeting(key, first: single(project[key]), second: single(target[key]), result: single(effectiveValues[key]))
}
let projectSettings = ["optimization": "none", "deployment": "15.0", "warnings": "all"]
let targetSettings = ["optimization": "speed", "deployment": "17.0"]
let settingMeetings = ["optimization", "deployment", "warnings"].map {
settingMeeting($0, project: projectSettings, target: targetSettings)
}
for meeting in settingMeetings { meeting.report() }
summarize(settingMeetings)
optimization: ilk none | ikinci speed | sonuc speed -> oncelik deployment: ilk 15.0 | ikinci 17.0 | sonuc 17.0 -> oncelik warnings: ilk all | ikinci bos | sonuc all -> olcum-disi oncelik: 2 kesisim: 0 birlesim: 0 ret: 0
İki farklı değer buluştuğunda hedefinki kazandı: iki buluşma öncelik çıktı. Üçüncü satır ise bir buluşma sayılmadı. Uyarı düzeyi hedefte hiç yazılmadığı için ikinci tanım boştu ve sınıflandırıcı bunu ölçüm dışı bıraktı. Bu doğru bir sonuçtur: yazılmamış bir ayar bir tanım değildir, yalnızca devralınmanın zeminidir.
Bu bulgunun sonucu pratik bir kuraldır. Projede yazılan bir ayarı hedefte yeniden yazmak, projedeki değeri yok sayar. Bir ayarı tüm hedeflerde tek yerden değiştirmek istiyorsanız hedeflerdeki aynı anahtarı kaldırmanız gerekir; aksi halde projedeki değişiklik hiçbir hedefe ulaşmaz.
GO1. Bu ölçüm anahtarı düz bir değer taşıyan ayarlar içindir. Değeri liste olan ayarlar bir sonraki bölümde ayrıca ölçülür. Değerin başka bir ayara referans veren biçimi ölçülmedi.
Devralma İşareti
Liste değerli ayarlar farklı davranır. Arama yolları buna iyi bir örnektir: derleyici başlık ve modül ararken bir dizin listesine bakar ve projeyle hedef bu listeye ayrı ayrı dizin ekleyebilir. Hedef kendi listesini yazarsa projenin listesini ezer. Ama hedef listesine devralma işaretini koyarsa, yani $(inherited) ifadesini yazarsa, platform bu işareti projenin listesiyle değiştirir. Aynı yazım yeri iki ayrı çözüme gider: işaret yoksa öncelik, işaret varsa birleşim.
Model bu değiştirmeyi bir düzleştirme olarak kurar: hedef listesindeki her eleman, işaretse projenin listesine, değilse kendisine açılır. Buluşmada ilk tanım projenin dizinleridir, ikinci tanım hedefin kendi yazdığı dizinler, yani işaretin dışındakiler.
// main.swift — devralma isareti ust duzeyin listesini hedef listesine katar
func expandPaths(project: [String], target: [String]) -> [String] {
target.flatMap { $0 == "$(inherited)" ? project : [$0] }
}
func pathMeeting(_ label: String, project: [String], target: [String]) -> Meeting {
let own = Set(target.filter { $0 != "$(inherited)" })
return Meeting(label, first: Set(project), second: own, result: Set(expandPaths(project: project, target: target)))
}
let projectPaths = ["libs/shared", "libs/vendor"]
let withInherited = pathMeeting("with-inherited", project: projectPaths, target: ["$(inherited)", "src/local"])
let withoutInherited = pathMeeting("without-inherited", project: projectPaths, target: ["src/local"])
withInherited.report()
withoutInherited.report()
with-inherited: ilk libs/shared,libs/vendor | ikinci src/local | sonuc libs/shared,libs/vendor,src/local -> birlesim without-inherited: ilk libs/shared,libs/vendor | ikinci src/local | sonuc src/local -> oncelik
Hedef listesinin içeriği iki durumda da src/local idi; fark yalnız işaretti. İşaretle birlikte sonuç üç dizindir ve çözüm birleşim çıktı. İşaret olmadan sonuç hedefin listesidir ve çözüm yine öncelik. Aynı alanda iki çözümün yan yana durabilmesi, ayarın türüne bakmadan çözümü tahmin etmenin yanıltıcı olacağını gösterir: düz değerde öncelik tek seçenektir, listede işaret iki seçenek arasında seçim yapar.
İşaretin yokluğu sessiz bir hata kaynağıdır. Proje düzeyinde eklenen bir üçüncü taraf dizini, hedefin listesi işaretsiz yazıldığı için derlemeye hiç girmez ve bunun belirtisi derleyicinin başlığı bulamamasıdır. Hata iletisi hedefin listesini değil eksik başlığı gösterir; bu yüzden bu tür sorunlar çoğu zaman yanlış yerde aranır.
GO2. Ölçüm arama yolları için ve iki düzeyli devralma için. Daha çok katmanlı devralma zincirinde işaretin her katmanda ayrı genişlediği ölçülmedi. İşaretin birden çok kez yazıldığı liste de ölçülmedi.
Platform Sürümleri
Asgari sürüm bir küme değil, tek bir değerdir. Dersin başındaki ölçüm bunu zaten gösterdi: proje 15.0, hedef 17.0 yazdığında geçerli değer 17.0 oldu ve çözüm öncelik çıktı. Proje ile hedef arasında sürümler kesişmez; hedefin asgari sürümü projeninkini ezer.
Kesişim başka bir buluşmada ortaya çıkar. Önceki konunun son dersinde uygulamanın desteklediği sürümleri, uygulamanın sınandığı sürümler olarak tanımlamıştık. Bu kümenin alt ucu hedefin geçerli asgari sürümüdür. Öteki tanım geliştirme ortamının elindeki çalıştırma hedefleri (run destination), yani uygulamanın üzerinde çalıştırılabileceği simülatörlerin ve aygıtların sürümleridir. Uygulama ancak iki kümede birden bulunan bir sürümde çalıştırılabilir: desteklenmeyen sürümdeki bir simülatör çalıştırma için seçilemez, elde bulunmayan bir sürüm de sınanamaz. Model bu yüzden iki kümenin kesişimini alır.
// main.swift — uygulamanin destekledigi surumlerle calistirma hedeflerinin surumleri kesisir
func runnableVersions(supported: Set<String>, destinations: Set<String>) -> Set<String> {
supported.intersection(destinations)
}
let appSupported: Set<String> = ["17", "18"]
let destinationVersions: Set<String> = ["15", "16", "17"]
let rangeMeeting = Meeting(
"deployment-range",
first: appSupported,
second: destinationVersions,
result: runnableVersions(supported: appSupported, destinations: destinationVersions)
)
rangeMeeting.report()
deployment-range: ilk 17,18 | ikinci 15,16,17 | sonuc 17 -> kesisim
Çözüm kesişim. Uygulama on sekizi destekliyor ama elde on sekiz sürümlü bir çalıştırma hedefi yok; elde on beş ve on altı sürümlü hedefler var ama uygulama onları desteklemiyor. İki küme yalnız on yedide buluşuyor. Bu kümenin boş çıkması da mümkündür: çalıştırma hedeflerinin hiçbiri desteklenen bir sürümde değilse sonuç boş olur ve çözüm retten başka bir şey olamaz. Böyle bir durumda uygulama derlenebilir, ama çalıştırılacak bir hedef bulunmadığı için hata ayıklama oturumu açılmaz.
GO3. Ölçüm sürümleri kesin değerlerden oluşan bir küme olarak ele aldı. Sürüm aralıklarının üst sınırla yazıldığı ve ondalık sürümlerin karşılaştırıldığı biçim bu derste ölçülmedi; paket yöneticisi dersinin konusudur.
Çalıştırma Şeması
Şema bu üç ayardan farklı bir şey yapar. Proje ve hedef ayar tanımlar; şema ise bir eylem için, örneğin çalıştırma ya da arşivleme için, hangi derleme yapılandırmasının (build configuration) kullanılacağını söyler. Yapılandırma, ayarların bir paketidir: hata ayıklama yapılandırması en iyilemeyi kapatır, yayın yapılandırması açar. Şema ayar tanımlamaz; hedeflerin zaten taşıdığı yapılandırmalardan birini seçer.
Bu fark ölçülebilir. Şemanın seçimini de iki küme olarak yazabiliriz: ilk tanım projenin sunduğu yapılandırmalar, ikinci tanım şemanın seçtiği. Seçim, sunulanın bir parçasıdır.
Sınırlayıcı Ölçüm
Şema bir tanım değil seçim olduğu için bu iddia bir ölçümle sınanmalıdır. Aşağıdaki modelde proje iki yapılandırma sunar ve şema iki eylem için birer yapılandırma seçer. Her eylem bir buluşma olarak kurulur; sınıflandırıcı seçimi bir tanım gibi ele alırsa bir çözüm adı bulması gerekir.
// main.swift — sema bir tanim degil secimdir; iki tanim yoksa bulusma yoktur
func pickConfiguration(action: String, scheme: [String: String]) -> String {
scheme[action, default: "yok"]
}
let available: Set<String> = ["debug", "release"]
let scheme = ["run": "debug", "archive": "release"]
let schemeMeetings = ["run", "archive"].map { action -> Meeting in
let chosen = pickConfiguration(action: action, scheme: scheme)
return Meeting("scheme-" + action, first: available, second: [chosen], result: [chosen])
}
for meeting in schemeMeetings { meeting.report() }
summarize(schemeMeetings)
scheme-run: ilk debug,release | ikinci debug | sonuc debug -> olcum-disi scheme-archive: ilk debug,release | ikinci release | sonuc release -> olcum-disi oncelik: 0 kesisim: 0 birlesim: 0 ret: 0
İki eylem de ölçüm dışı kaldı ve dört sayaç da sıfırda durdu. Seçilen yapılandırma her seferinde sunulanın bir alt kümesiydi; bu yüzden sınıflandırıcı bir buluşma görmedi. Şemanın geçerli ayarlar üzerindeki etkisi gerçektir, ama bu etki bir buluşmanın çözümü değil, bir seçimdir: bir yapılandırmayı seçmek, o yapılandırmanın ayarlarını devreye sokar ve ayarların kendi buluşmaları bu bölümün başındaki kurallarla çözülür.
Bu, dersin kuralının sınırını çizer. Kural iki tanım bulunduğunda çözümü adlandırır; tanım bulunmayan yerde sessiz kalır. Şemada bir ayarın yazılması mümkündür, örneğin bir ortam değişkeni, ancak o durumda şema bir tanım sahibi olur ve yeni bir buluşma doğar. Ortam değişkenleri hata ayıklama konusunun işidir.
GO4. Bu ölçüm şemanın seçim yapan hâli içindir. Şemanın kendi ayarlarını taşıdığı hâl ve bir eylemin birden çok yapılandırmayı birlikte kullandığı durum ölçülmedi.
Özet
- Aynı derleme ayarı projede ve hedefte tanımlıysa hedef düzeyi kazanır; yalnız projede yazılan ayar hedefe devralınır ve buluşma doğurmaz.
- Liste değerli ayarlarda devralma işareti çözümü belirler: işaretle birleşim, işaretsiz öncelik.
- Asgari sürüm tek değerlidir ve hedefte ezilir; uygulamanın desteklediği sürümlerle çalıştırma hedeflerinin sürümlerinin kesişimi uygulamanın çalıştırılabildiği sürümlerdir, kesişim boşsa sonuç ret olur.
- Çalıştırma şeması bir tanım değil bir seçimdir; seçilen yapılandırma sunulanın alt kümesi olduğu için buluşma saymaz.
- Bu dersin çözümü: öncelik. Aynı derleme ayarı iki düzeyde tanımlandığında hedef düzeyi projenin değerini ezer.
Sonraki Adım
Bu derste projenin ve hedefin ayarları buluştu. Bir uygulamanın ayarları yalnız bu iki yerde durmaz: şablondan gelen bir özellik listesi, derlemenin kendi ürettiği anahtarlar ve aynı hedefe eklenen kaynak grupları da aynı soruyu doğurur. Bir sonraki ders proje dosya yapısında şablonun yazdığı anahtarlarla derlemenin ürettiği anahtarların buluşmasını ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.