Prima del rilascio di Android 7.0, Android utilizzava GNU Make esclusivamente per descrivere ed eseguire le sue regole di compilazione. Il sistema di build Make è ampiamente supportato e utilizzato, ma su scala Android è diventato lento, soggetto a errori, non scalabile e difficile da testare. Il sistema di build Soong offre la flessibilità necessaria per le build Android.
Per questo motivo, ci si aspetta che gli sviluppatori della piattaforma abbandonino Make e adottino Soong il prima possibile. Invia domande al gruppo Google creato per Android per ricevere supporto.
Cos'è Soong?
Il sistema di build Soong è stato introdotto in Android 7.0 (Nougat) per sostituire Make. Sfrutta lo strumento clone Kati GNU Make e il componente del sistema di build Ninja per velocizzare le build di Android.
Consulta la descrizione del sistema Android Make Build nel progetto Android Open Source (AOSP) per istruzioni generali e modifiche al sistema di build per gli scrittori Android.mk per informazioni sulle modifiche necessarie per l'adattamento da Make a Soong.
Consulta le voci relative alla build nel glossario per le definizioni dei termini chiave e i file di riferimento di Soong per i dettagli completi.
Confronto Make e Soong
Ecco un confronto tra la configurazione Make e Soong che ottiene lo stesso risultato in un file di configurazione Soong (Blueprint o .bp
).
Fai l'esempio
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libxmlrpc++
LOCAL_MODULE_HOST_OS := linux
LOCAL_RTTI_FLAG := -frtti
LOCAL_CPPFLAGS := -Wall -Werror -fexceptions
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/src
LOCAL_SRC_FILES := $(call \
all-cpp-files-under,src)
include $(BUILD_SHARED_LIBRARY)
Un buon esempio
cc_library_shared {
name: “libxmlrpc++”,
rtti: true,
cppflags: [
“-Wall”,
“-Werror”,
“-fexceptions”,
],
export_include_dirs: [“src”],
srcs: [“src/**/*.cpp”],
target: {
darwin: {
enabled: false,
},
},
}
Consulta Configurazione di build semplice per esempi di configurazione Soong specifici del test.
Formato file Android.bp
In base alla progettazione, i file Android.bp
sono semplici. Non contengono condizionali o istruzioni di flusso di controllo; tutta la complessità è gestita dalla logica di creazione scritta in Go. Quando possibile, la sintassi e la semantica dei file Android.bp
sono simili ai file Bazel BUILD .
Moduli
Un modulo in un file Android.bp
inizia con un tipo di modulo seguito da un insieme di proprietà nel name: "value",
formato:
cc_binary {
name: "gzip",
srcs: ["src/test/minigzip.c"],
shared_libs: ["libz"],
stl: "none",
}
Ogni modulo deve avere una proprietà name
e il valore deve essere univoco in tutti i file Android.bp
, ad eccezione dei valori della proprietà name
negli spazi dei nomi e nei moduli predefiniti, che potrebbero ripetersi.
La proprietà srcs
specifica i file sorgente utilizzati per creare il modulo, come un elenco di stringhe. Puoi fare riferimento all'output di altri moduli che producono file sorgente, come genrule
o filegroup
, utilizzando la sintassi di riferimento del modulo ":<module-name>"
.
Per un elenco dei tipi di moduli validi e delle relative proprietà, consultare la Guida di riferimento dei moduli Soong .
Tipi
Le variabili e le proprietà sono fortemente tipizzate, con variabili basate dinamicamente sulla prima assegnazione e proprietà impostate staticamente dal tipo di modulo. I tipi supportati sono:
- Booleani (
true
ofalse
) - Numeri interi (
int
) - Stringhe (
"string"
) - Elenchi di stringhe (
["string1", "string2"]
) - Mappe (
{key1: "value1", key2: ["value2"]}
Le mappe possono contenere valori di qualsiasi tipo, comprese le mappe nidificate. Elenchi e mappe possono contenere virgole finali dopo l'ultimo valore.
Globi
Le proprietà che accettano un elenco di file, come srcs
, possono anche accettare modelli glob. I modelli globali possono contenere il normale carattere jolly UNIX *
, ad esempio *.java
. I modelli globali possono anche contenere un singolo carattere jolly **
come elemento del percorso, che corrisponde a zero o più elementi del percorso. Ad esempio, java/**/*.java
corrisponde sia ai modelli java/Main.java
che java/com/android/Main.java
.
Variabili
Un file Android.bp
può contenere assegnazioni di variabili di primo livello:
gzip_srcs = ["src/test/minigzip.c"],
cc_binary {
name: "gzip",
srcs: gzip_srcs,
shared_libs: ["libz"],
stl: "none",
}
L'ambito delle variabili è il resto del file in cui sono dichiarate, nonché qualsiasi file Blueprint figlio. Le variabili sono immutabili con un'eccezione: possono essere aggiunte con un'assegnazione +=
, ma solo prima che venga fatto riferimento.
Commenti
//
file Android.bp
possono contenere commenti /* */
a riga singola in stile C e // a riga singola in stile C++.
Operatori
Stringhe, elenchi di stringhe e mappe possono essere aggiunti utilizzando l'operatore +. Gli interi possono essere sommati utilizzando l'operatore +
. L'aggiunta di una mappa produce l'unione delle chiavi in entrambe le mappe, aggiungendo i valori di tutte le chiavi presenti in entrambe le mappe.
Condizionali
Soong non supporta i condizionali nei file Android.bp
. Invece, la complessità delle regole di compilazione che richiederebbero condizionali viene gestita in Go, dove è possibile utilizzare funzionalità del linguaggio di alto livello e tenere traccia delle dipendenze implicite introdotte dai condizionali. La maggior parte dei condizionali viene convertita in una proprietà della mappa, in cui uno dei valori nella mappa viene selezionato e aggiunto alle proprietà di livello superiore.
Ad esempio, per supportare file specifici dell'architettura:
cc_library {
...
srcs: ["generic.cpp"],
arch: {
arm: {
srcs: ["arm.cpp"],
},
x86: {
srcs: ["x86.cpp"],
},
},
}
Formattatore
Soong include un formattatore canonico per i file Blueprint, simile a gofmt . Per riformattare in modo ricorsivo tutti i file Android.bp
nella directory corrente, esegui:
bpfmt -w .
Il formato canonico include rientri di quattro spazi, nuove righe dopo ogni elemento di un elenco multielemento e una virgola finale negli elenchi e nelle mappe.
Moduli speciali
Alcuni gruppi di moduli speciali hanno caratteristiche uniche.
Moduli predefiniti
Un modulo predefinito può essere utilizzato per ripetere le stesse proprietà in più moduli. Per esempio:
cc_defaults {
name: "gzip_defaults",
shared_libs: ["libz"],
stl: "none",
}
cc_binary {
name: "gzip",
defaults: ["gzip_defaults"],
srcs: ["src/test/minigzip.c"],
}
Moduli precostruiti
Alcuni tipi di moduli predefiniti consentono a un modulo di avere lo stesso nome delle sue controparti basate sul codice sorgente. Ad esempio, può esserci un cc_prebuilt_binary
denominato foo
quando esiste già un cc_binary
con lo stesso nome. Ciò offre agli sviluppatori la flessibilità di scegliere quale versione includere nel loro prodotto finale. Se una configurazione di build contiene entrambe le versioni, il valore del flag prefer
nella definizione del modulo predefinito determina quale versione ha la priorità. Tieni presente che alcuni moduli predefiniti hanno nomi che non iniziano con prebuilt
, come android_app_import
.
Moduli dello spazio dei nomi
Fino alla conversione completa di Android da Make a Soong, la configurazione del prodotto Make deve specificare un valore PRODUCT_SOONG_NAMESPACES
. Il suo valore dovrebbe essere un elenco di spazi dei nomi separati da spazi che Soong esporta in Make per essere creato dal comando m
. Una volta completata la conversione di Android a Soong, i dettagli sull'abilitazione degli spazi dei nomi potrebbero cambiare.
Soong offre la possibilità ai moduli in directory diverse di specificare lo stesso nome, purché ciascun modulo sia dichiarato all'interno di uno spazio dei nomi separato. Uno spazio dei nomi può essere dichiarato in questo modo:
soong_namespace {
imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
}
Tieni presente che uno spazio dei nomi non ha una proprietà name; il suo percorso viene assegnato automaticamente come nome.
A ogni modulo Soong viene assegnato uno spazio dei nomi in base alla sua posizione nell'albero. Ogni modulo Soong è considerato nello spazio dei nomi definito da soong_namespace
trovato in un file Android.bp
nella directory corrente o nella directory dell'antenato più vicina. Se non viene trovato alcun modulo soong_namespace
, il modulo viene considerato nello spazio dei nomi root implicito.
Ecco un esempio: Soong tenta di risolvere la dipendenza D dichiarata dal modulo M nello spazio dei nomi N che importa gli spazi dei nomi I1, I2, I3...
- Quindi, se D è un nome completo nel formato
//namespace:module
, viene cercato solo lo spazio dei nomi specificato per il nome del modulo specificato. - Altrimenti, Soong cerca prima un modulo chiamato D dichiarato nel namespace N.
- Se quel modulo non esiste, Soong cerca un modulo chiamato D negli spazi dei nomi I1, I2, I3...
- Infine, Soong esamina lo spazio dei nomi root.