• Non ci sono risultati.

Gradle, Kotlin e lo Sviluppo di un’Applicazione Multipiattaforma

N/A
N/A
Protected

Academic year: 2021

Condividi "Gradle, Kotlin e lo Sviluppo di un’Applicazione Multipiattaforma"

Copied!
78
0
0

Testo completo

(1)

ALMA MATER STUDIORUM – UNIVERSIT `

A DI BOLOGNA

CAMPUS DI CESENA

Scuola di Ingegneria e Architettura

Corso di Laurea in Ingegneria e Scienze Informatiche

Gradle, Kotlin e lo Sviluppo

di un’Applicazione Multipiattaforma

Tesi di laurea in

Programmazione ad Oggetti

Relatore

Chiar.mo Prof. Andrea Omicini

Correlatore

Dott. Giovanni Ciatto

Candidato

Mariano Caldara

Seconda Sessione di Laurea

Anno Accademico 2018-2019

(2)
(3)

Abstract

Gestire un applicazione multipiattaforma `e una operazione onerosa vista la diver-sit`a delle tecnologie e delle piattaforme con le quali fa fronte, tuttavia `e diventato l’obbiettivo e il fine di molti progetti, che in termini di guadagno possono godere di un minor codice implementativo e di una maggiore utenza finale, raggiungibile in tempi di produzione minore. Gralde che `e uno strumento navigato nella gestione del codice, ha collaborato con JetBrains nella costruzione di un progetto pi`u grande nominato Kotlin Multipiattaforma, che coinvolge non soltanto lo strumento in s`e ma anche la piattaforma di sviluppo intelliJ IDEA, creando un connubio perfetto per la creazione dei progetti multipiattaforma. L’obbiettivo di questo documento non `e soltanto riconoscere questi strumenti ed esplicarli ma avere anche un ruolo guida nella costruzione di un progetto di questo tipo, infatti lo studio di questi argomenti `e stato unito alle sperimentazioni su diversi progetti, infatti nei capitoli finali sar`a evidente il contributo personale.

(4)
(5)

Un giorno mio padre venne a prendermi a scuola e mi port`o al mare. Era troppo freddo per fare il bagno, cos`ı ci sedemmo sull’asciugamano a mangiare la pizza. Quando tornai a casa avevo le scarpe piene di sabbia e sporcai il pavimento della mia stanza. Avevo sei anni neanche me ne accorsi. Mia madre mi sgrid`o per tutto quel casino, invece lui non era arrabbiato. Disse che miliardi di anni fa, la rotazione della terra e il movimento degli oceani avevano portato quella sabbia in quel punto della spiaggia e che io l’avevo portata via. Disse: “Tutti i giorni cambiamo il mondo”, ma non riesco a pensare in quanti giorni di quante vite riuscirei a portare una scarpa piena di sabbia a casa fino a svuotare la spiaggia, finch´e questo non faccia la differenza. Tutti i giorni cambiamo il mondo, ma perch´e il cambiamento sia significativo ci vuole pi`u tempo di quanto ne abbiamo. Non accade mai niente in una sola volta. E’ lento, `e metodico, `e estenuante, non tutti abbiamo lo stomaco per farlo.

tratto da Mr. Robot

(6)
(7)

Ringraziamenti

Ringrazio il mio relatore Andrea Omicini e il mio correlatore Giovanni Ciatto per avermi ispirato e assegnato il progetto. Infine, ringrazio i miei amici e la mia famiglia per avermi supportato.

(8)
(9)

Indice

Abstract iii

1 Introduzione 1

2 Stato dell’arte 3

2.1 Kotlin, e i motivi del successo . . . 3

2.2 Linguaggi DSL e Kotlin Script DSL . . . 6

2.3 Automazione del progetto . . . 9

2.3.1 Gradle, API principali . . . 9

2.3.2 Gestione delle dipendenze . . . 13

2.3.3 Estensione delle funzionalit`a di Gradle . . . 22

3 Applicazioni multipiattaforma 25 3.1 Logica di base . . . 26

3.1.1 Meccanismo Expect/Actual . . . 28

3.2 Setup di un progetto multipiattaforma con Gradle . . . 30

3.2.1 Configurazione dei Target . . . 33

3.2.2 Configurazione dei Source Sets . . . 34

3.2.3 Criticit`a e Interoperabilit`a in JVM e JS . . . 39

4 Test e Deploy di una libreria multipiattaforma 43 4.1 Testing . . . 43

4.2 Deploy . . . 47

4.2.1 Maven Repository e Ivy Repository . . . 49

4.2.2 NPM . . . 52

5 Caso di studio: Kt-math 55

6 Conclusioni 61

Bibliografia 63

(10)
(11)

Elenco delle figure

2.1 Statistiche di popolarit`a tra i build tools . . . 10

2.2 Interfaccia di Project, con i metodi principali . . . 11

2.3 Tasks e Project . . . 11

2.4 Rappresentazione DAG dei tasks . . . 12

2.5 Interfaccia con metodi principali di Task . . . 12

2.6 Grafico delle dipendenze della libreria Hibernate . . . 14

2.7 Interfacce rilevanti nelle API di Gradle per la gestione dei repositories 18 3.1 Distribuzione di Kotlin su pi`u piattaforme JS/JVM based . . . 26

3.2 Per ogni target, due compilazioni di default, main e test, e i relativi source set anch’essi di base . . . 27

3.3 Moduli e i loro artefatti . . . 32

4.1 Report in HTML dei risultati dei test, generati attraverso il task ”allTests” . . . 47

(12)
(13)

Listati

2.1 HelloWorld in Java . . . 4

2.2 HelloWorld in Kotlin . . . 4

2.3 Creazione class Person in Java . . . 4

2.4 Creazione class Person in Kotlin . . . 5

2.5 Lambda label . . . 6

2.6 High Order Function . . . 7

2.7 Stesura finale Kotlin DSL . . . 8

2.8 Creazione di task . . . 13

2.9 Esempio di configurazione delle dipendenze . . . 14

2.10 Dipendenze da file locali . . . 16

2.11 Dipendenze tra progetti . . . 17

2.12 Dichiarazione dei repository Maven . . . 18

2.13 Dichiarazione dei repositery Ivy . . . 19

2.14 Dichiarazione repository locale . . . 20

2.15 Autenticazione tramite credenziali . . . 20

2.16 Gestione dei conflitti tra dipendenze . . . 21

2.17 Diversi modi di applicare un plugin . . . 23

3.1 Esempio di uso expect nel codice comune . . . 28

3.2 Implementazione di writeLogMessagesu ogni piattaforma spe-cifica . . . 29

3.3 kotlin-multiplatform plugin . . . 30

3.4 Configurazione dei target nel file gradle.build.kts . . . 30

3.5 Configurazioni dei target con le funzioni prestabilite di Kotlin . . . 33

3.6 Diversi modi di accedere ai target creati . . . 34

3.7 Configurazione del build.file per creare e modificare un source set esistente . . . 34

3.8 Target personalizzati e relazioni di dipendenza . . . 36

3.9 Esempio di configurazione delle dipendenze dei source set ”common-Main” e ”js”common-Main” . . . 37

3.10 Dichiarazione alternativa delle dipendenze attraverso la funzione top-level di Gradle DSL . . . 38

(14)

3.11 Impostazioni relativi ai sorgenti . . . 38

3.12 Companion Object essendo un oggetto pu`o essere esteso o imple-mentare delle interfacce . . . 40

3.13 Uso di @JsName() . . . 40

4.1 Configurazione base nella gestione delle dipendenze per kotlin.test . 44 4.2 Esempio di test in Kotlin . . . 45

4.3 DSL configurativo per eseguire test in JS, Karma pu`o essere sosi-tuito con Mocha o NodeJs . . . 46

4.4 Configurazione di Dokka di un progetto multipiattaforma tramite Gradle . . . 48

4.5 Maven Pubblish Plugin . . . 49

4.6 Configurazione personalizzata per pubblicare su repository Maven e includere la documentazione Dokka . . . 50

4.7 Gli identificativi di base possono essere cambiati . . . 51

4.8 Plugin ”com.moowork.node” . . . 52

4.9 Task per pubblicare un package su npm . . . 52

5.1 Filtra e copia tutti i file js contenuti nelle dipendenze e usati durante la compilazione del target JS nella cartella node modules contenuta nella folder build . . . 56

5.2 Jest pu`o essere cambiato con Mocha, Karma, Jasmine o Tape . . . 57

5.3 Lo script accetta come argomento i sorgenti js del test . . . 57

5.4 L’ambiente esecutivo `e il browser ma poteva essere adoperato anche nodeJS . . . 58

(15)

Capitolo 1

Introduzione

Con la continua crescita di dispositivi e sistemi sempre pi`u diversi tra loro, `e nata la necessit`a di creare applicazioni eseguibili su pi`u piattaforme. Questo bisogno, dettato dalle aziende nel compito di raggiungere pi`u utenti possibili, si `e tramutato nell’obbiettivo, per i programmatori, di creare un codice pi`u universale possibile e che potesse essere distribuito su pi`u sistemi.

Questa tendenza porta diversi benefici per gli sviluppatori come riusabilit`a, co-sti e tempi di produzione migliori, mentre per gli utenti finali avranno una maggiore possibilit`a di godere di un’applicazione simile o addirittura uguale, indipendente-mente dal sistema.

Kotlin `e un linguaggio che al pi`u si appresta a dare vita ad un’applicazio-ne multipiattaforma, ultimamente questo linguaggio ha raggiunto una notevole popolarit`a, il motivo `e riconducibile a due eventi precisi, il supporto ufficiale di Google per Android (Cleron, 2017), e l’integrazione in Spring Boot 2 (Deleuze, 2017). Tuttavia, il suo successo nel campo dei progetti multipiattaforma `e dovuto soprattutto a Gradle e JetBrains, che offrono un’integrazione completa con questo linguaggio.

L’obbiettivo di questa tesi non `e soltanto mostrare il lato implementativo, che comporr`a comunque la parte fondamentale, ma fornire anche una panoramica di questi strumenti, cercando quindi di avere un ruolo guida nel creare un’applicazione multipiattaforma con Gradle e Kotlin.

(16)

Il fine di questo progetto inoltre `e analizzare e comprendere questo tipo di tecnologia nel suo processo di sviluppo, soffermandosi soprattutto sulla fase di testing e deploy.

(17)

Capitolo 2

Stato dell’arte

Durante la programmazione si riconduce, spesso, nel codice la chiave del successo di un ottimo progetto software. Nonostante la qualit`a di quest’ultimo sia un elemen-to imprescindibile nello sviluppo del codice, una visione globale e ingegneristica suggerirebbe l’adozione di ulteriori strumenti per la produttivit`a del programma-tore. Uno su tutti `e Gradle, oggetto di studio approfondito di questa tesi, `e uno strumento che si pone come sistema per automatizzare diverse fasi dello sviluppo del codice, ricalcando la logica di sistemi gi`a navigati come Apache Ant e Apache Maven, apportando per`o diversi miglioramenti su pi`u punti. Per comprendere al meglio questi miglioramenti, in questo capitolo si analizzer`a lo strumento in que-stione prima in un’ottica generale e successivamente nell’ambito degli applicativi multipiattaforma.

Per conferire una visione pi`u chiara dello strumento verr`a prima introdotto Kotlin, nello specifico Kotlin DSL, come linguaggio perfetto per la sua configurazione. La divisione tra i due argomenti non sar`a netta ma combinata per dare una visione pi`u chiara e omogenea.

2.1

Kotlin, e i motivi del successo

Kotlin, `e un linguaggio general-purpose, multi-paradigma e fortemente tipizzato; si ispira apertamente a Java, garantendone una completa compatibilit`a, infatti

(18)

pu`o essere compilato su JVM, e a livello di sintassi si presenta anche molto simile. Questa somiglianza e compatibilit`a ha permesso una veloce diffusione e adozio-ne da parte di aziende del calibro di Google, Amazon, Netflix; tuttavia il ruolo di questo linguaggio non `e soltanto da copione di Java, ma introduce rispetto a quest’ultimo numerose funzionalit`a che sopperiscono ai limiti del linguaggio svi-luppato da Microsystems.

Per esempio, in primis, si presenta come un linguaggio pi`u sintetico e veloce rispetto al linguaggio di Oracle.

Listato 2.1: HelloWorld in Java

c l a s s H e l l o W o r l d {

p u b l i c s t a t i c void main ( S t r i n g [] args ) { S y s t e m . out . p r i n t l n (" H e l l o W o r l d ! ") ; }

}

Listato 2.2: HelloWorld in Kotlin

p a c k a g e h e l l o w o r l d

fun main () {

p r i n t l n (" H e l l o W o r l d ! ") }

Un esempio comune e lampante si pu`o ritrovare nella creazione delle classi, difatti una classe in Kotlin, automaticamente assegna ad ogni attributo detto “property” i suoi metodi getters e setters in automatico.

Listato 2.3: Creazione class Person in Java

c l a s s P e r s o n {

p r i v a t e S t r i n g name ;

p r i v a t e S t r i n g s u r n a m e ;

p u b l i c P e r s o n () {

(19)

2.1. KOTLIN, E I MOTIVI DEL SUCCESSO 5

p u b l i c P e r s o n ( S t r i n g name , S t r i n g s u r n a m e ) {

this. name = name ;

this. s u r n a m e = s u r n a m e ; } p u b l i c S t r i n g g e t N a m e () { r e t u r n name ; } p u b l i c void s e t N a m e ( S t r i n g name ) {

this. name = name ; } p u b l i c S t r i n g g e t S u r n a m e () { r e t u r n s u r n a m e ; } p u b l i c void s e t S u r n a m e ( S t r i n g s u r n a m e ) { this. s u r n a m e = s u r n a m e ; } }

Listato 2.4: Creazione class Person in Kotlin

c l a s s P e r s o n (var name : String, var s u r n a m e : S t r i n g)

Inoltre, il linguaggio di JetBrains mira a superare i limiti di Java, infatti gode di un’architettura di base pi`u solida e sicura, che lo permette di ridurre le ecce-zioni a runtime. Nel concreto, le variabili non possono essere, per impostazione predefinita, istanziate a null. Questa caratteristica detta null-safety, permette al compilatore di verificare ogni null-reference prima di eseguire il codice.

Le potenzialit`a presentate sono solo una minima parte di ci`o che questo linguaggio pu`o offrire, tuttavia quelle che sono fondamentali per lo studio di un’applicazio-ne multipiattaforma sono Kotlin DSL e Kotlin Multipiattaforma, che verranno analizzate nei paragrafi a seguire.

(20)

2.2

Linguaggi DSL e Kotlin Script DSL

Un linguaggio general purpose come Kotlin visto fin’ora, `e un tipo di linguaggio adatto a programmare ogni tipo di applicazione, indipendentemente dal suo do-minio, tuttavia diverse funzionalit`a permettono a Kotlin di creare un linguaggio domain specific, appunto specifico per un determinato dominio applicativo. Kotlin DSL, come il resto dei linguaggi domain-specific, `e espressivo e facilmente leggibile, principalmente molto simile ad una libreria API, differendosi da quest’ultima per l’uso di una determinata grammatica o struttura ben definita. Le funzionalit`a che permettono di rendere Kotlin un linguaggio pi`u semplice da comprendere anche a chi non programma sono:

• Espressioni lambda • Funzione High-Order

• Espressioni lambda con ricevitore

Nei paragrafi seguenti si analizzer`a Kotlin DSL nello specifico poich´e ampiamente adoperato nella configurazione dello strumento Gradle.

Seguiranno esempi pratici per comprendere al meglio le funzionalit`a elencate. Le espressioni lambda sono essenzialmente funzioni anonime che possono essere trattate come valori, accettano un numero predefinito di parametri e ritornano un unico valore automaticamente.

Listato 2.5: Lambda label

/* L o g i c a di base : ( L i s t a di p a r a m e t r i ) -> Tipo di v a l o r e r e s t i t u i t o */ () -> Unit // N e s s u n p a r a m e t r o , r e s t i t u i s c e Unit , e q u i v a l e n t e di void

(21)

2.2. LINGUAGGI DSL E KOTLIN SCRIPT DSL 7 (Int) -> Int // a c c e t t a i n t e r o e r e s t i t u i s c e i n t e r o

(S t r i n g) -> Unit // S t r i n g a per Unit

val l a m b d a : (S t r i n g) -> Unit = { p r i n t l n (it) }

l a m b d a (" H e l l o ")

Essendo appunto valori, le lambda possono essere sia parametri che valori di ritor-no di una funzione, e in questi casi si introduce il concetto di High-Order Function. Per ridurre ulteriormente la sintassi, se la lambda in questione `e l’ultimo parame-tro, questa pu`o essere spostata fuori dalle parentesi tonde e se `e anche l’unico parametro, le parantesi possono essere omesse.

Listato 2.6: High Order Function

c l a s s D e p e n d e n c y H a n d l e r { fun c o m p i l e ( c o o r d i n a t e : S t r i n g) { // a g g i u n g e c o o r d i n a t e di una c o l l e z i o n e } fun t e s t C o m p i l e ( c o o r d i n a t e : S t r i n g) { // a g g i u n g e c o o r d i n a t e di una c o l l e z i o n e } } fun d e p e n d e n c i e s ( a c t i o n : ( D e p e n d e n c y H a n d l e r ) -> Unit) : D e p e n d e n c y H a n d l e r { val d e p e n d e n c i e s = D e p e n d e n c y H a n d l e r () a c t i o n ( d e p e n d e n c i e s ) r e t u r n d e p e n d e n c i e s }

(22)

// d e p e n d e n c i e s ( { . . . } )

d e p e n d e n c i e s {

it. c o m p i l e (" ") // it i s t a n z a di D e p e n d e n c y H a n d l e r

it. t e s t C o m p i l e (" ") }

Nel caso del codice 2.6 la funzione dependencies accetta come parametro una lambda composta da come parametro di input un DependencyHandler e ritorna Unit (il correspettivo di void in Java), la funzione infine ritorna un’istanza di DependencyHandler. I linguaggi DSL, mirano ad eliminare le ripetizioni, infatti il codice presentato pu`o essere ancora migliorato, eliminando it mediante l’uso di una lambda con ricevitore. Questa funzionalit`a permette di scrivere il corpo di un’espressione lambda omettendo il riferimento it o this dell’oggetto in input, modificando quindi le propriet`a di un oggetto. La stesura finale, pu`o essere un esempio di configurazione delle dipendenze di un progetto tramite Kotlin DSL e Gradle.

Listato 2.7: Stesura finale Kotlin DSL

fun d e p e n d e n c i e s ( a c t i o n : D e p e n d e n c y H a n d l e r .() -> Unit) : D e p e n d e n c y H a n d l e r { val d e p e n d e n c i e s = D e p e n d e n c y H a n d l e r () d e p e n d e n c i e s . a c t i o n () r e t u r n d e p e n d e n c i e s } d e p e n d e n c i e s { c o m p i l e (" ") // this . c o m p i l e t e s t C o m p i l e (" ") }

In conclusione Kotlin DSL sta diventando sempre pi`u popolare nella configurazio-ne di Gradle (Carbonconfigurazio-nelle, 2019), e mira a sostituire Groovy1, apportando

(23)

2.3. AUTOMAZIONE DEL PROGETTO 9 se migliorie. Infatti, questo linguaggio che `e stato ideato grazie al connubio tra JetBrains e Gradle, permette una completa integrazione con gli IDE; supportando funzionalit`a base che Groovy non offre, come l’auto-completamento e il refactoring (Beams, 2016).

2.3

Automazione del progetto

Le tecniche di automazione nella produzione di artefatti durante lo sviluppo soft-ware di un progetto sono diventate sempre pi`u raffinate e determinanti per un progetto di successo. Queste operazioni, come produzione di codice binario, pac-kaging dello stesso, testing e produzione di documentazione venivano in un primo momento eseguite dal programmatore medianti stesura di script2, ma negli ultimi

anni vengono affidate a strumenti di Build Automation pi`u completi come Gradle. L’obbiettivo centrale di questi strumenti `e la possibilit`a di distribuire il codice in modo consistente e ripetibile, minimizzando l’intervento umano e gli errori durante la fase di compilazione.

Tra gli strumenti di build automation Gradle occupa un market share del 19% secondo il sondaggio di Paraschiv, 2017 ed `e ampiamente impiegato tra i program-matori come si denota dal grafico 2.1. In questo capitolo veranno illustrati gli elementi cardine di questa tecnologia e di questo strumento.

2.3.1

Gradle, API principali

Project `e l’elemento su cui verte il sistema Gradle, infatti `e la rappresentazione logica di ci`o che deve diventare artefatto, ovvero l’obbiettivo finale del lavoro di un programmatore in termini di sviluppo software. A livello tecnico, questo elemento si rappresenta come l’API principale, su cui vertono tutti i metodi di Gradle e quin-di le sue funzionalit`a, come creazione di Tasks o gestione delle dipendenze, questi e gli altri metodi a disposizione per il programmatore sono visibili nell’interfaccia

(24)

Figura 2.1: Statistiche di popolarit`a tra i build tools

2.2 e sono accessibili attraverso il file di build3. Questo tipo di file, non `e unico, e durante la fase di build per ognuno di questi, Gradle, seguendo i comandi descritti nello script, istanzia uno o pi`u Projects e i Tasks relativi. I primi, infatti, possono essere visti, come un’insieme dei secondi, e non sono altro che singole operazioni specifiche, eseguibili anche autonomamente, che susseguite tra loro portano alla creazione del artefatto. Questo susseguirsi di operazioni definisce il flusso di svolgi-mento dell’intero processo di build, e si modella su un grafo aciclico diretto (DAG), che evidenzia le dipendenze tra i Task, infatti spesso questi lavorano su artefatti di altri, in particolare nella struttura DAG, i nodi corrispondono alle unit`a di la-voro Tasks (per esempio produzione di documentazione), mentre gli archi diretti rappresentano le loro dipendenze (Task A depends on Task B ). Un nodo conosce soltanto il suo stato di esecuzione, e durante il flusso, pu`o essere eseguito solo una volta, indipendentemente se `e oggetto di pi`u dipendenze. Come si nota dalla sua interfaccia, figura 2.5, dove sono rappresentanti soltanto i metodi principali, ’DoFirst ’ e ’DoLast ’ permettono di eseguire le azioni in due momenti diversi. Per comprendere al meglio questi metodi, e il loro funzionamento e necessario intro-durre e comprendere il ciclo di vita della build di Gradle. Ogni build di Gradle si articola in tre fasi, nella prima detta appunto fase di inizializzazione, lo strumento

3gradle.build file di configurazione di Gradle scritto in DSL con Groovy o in Kotlin DSL

(25)

2.3. AUTOMAZIONE DEL PROGETTO 11

Figura 2.2: Interfaccia di Project, con i metodi principali

Figura 2.3: Tasks e Project

in questione una volta identificato il progetto o i progetti su cui lavorare, analiz-zando il file settings.gradle4istanzia per ognuno di questi un org.gradle.api.Project. Segue, quindi, la fase di configurazione; Gradle in questo frangente riconosce i

(26)

Figura 2.4: Rappresentazione DAG dei tasks

Figura 2.5: Interfaccia con metodi principali di Task

getti utili alla build, e costruisce, mediante i relativi build-script, il gi`a citato DAG. Il tempo di configurazione rispetto ad altri strumenti di build-automation `e gene-ralmente ridotto, questo perch`e questo strumento introduce una nuova tecnologia denominata ”Configuration on-demand”(Authoring Multi-Project Builds), che gli permette di configurare solo i progetti e i task utili alla build. Nell’ultima fase, vengono eseguiti i Task tenendo conto delle loro dipendenze, basandosi ovviamente sul DAG creato nella fase precedente.

(27)

2.3. AUTOMAZIONE DEL PROGETTO 13 In conclusione, segue una possibile creazione di Task in Kotlin DSL(2.2), concre-tizzando anche le definizioni dei metodi ”DoLast()” e ”DoFirst()”; in particolare, queste funzioni permettono di influenzare le azioni del task nel tempo di build, eseguendo l’azione rispettivamente all’inizio o alla fine della fase di esecuzione.

Listato 2.8: Creazione di task t a s k s . r e g i s t e r (" f i r s t T a s k ") { g r o u p = " Try " d e s c r i p t i o n = " T r y i n g doLast , d o F i r s t " d o F i r s t { p r i n t l n (" This is e x e c u t e d f i r s t d u r i n g the e x e c u t i o n p h a s e ") } d o L a s t { p r i n t l n (" This is e x e c u t e d last d u r i n g the e x e c u t i o n p h a s e ") } p r i n t l n (" This is e x e c u t e d d u r i n g the c o n f i g u r a t i o n p h a s e ") }

2.3.2

Gestione delle dipendenze

La maggior parte dei progetti dipende da librerie esterne, inizialmente, quando i programmi di build-automation non erano ancora popolari, era compito del grammatore includere e gestire le dipendenze esterne, aumentando i tempi di pro-duzione del software. Una visione pi`u ingegneristica, invece, vede la gestione delle dipendenze incluse nella definizione di build. In questo modo, Gradle `e a cono-scenza di ogni singola dipendenza, e riesce a gestire al meglio anche la transitivit`a di queste, operazione che manualmente risulterebbe eccessivamente laboriosa, so-prattutto in programmi complessi. Un esempio concreto pu`o essere la libreria Hibernate, come si denota dalla fig. 2.6, il programmatore non soltanto deve

(28)

co-noscere le dipendenze transitive, ma anche le loro versioni per evitare conflitti. Un

Figura 2.6: Grafico delle dipendenze della libreria Hibernate

esempio di configurazione, in Kotlin DSL, delle dipendenze `e il listato 2.9, dove vengono introdotti tre concetti fondamentali, configurazione (api, implementation, testImplementation sempre riferendosi al listato 2.9), dipendenza (junit:junit:4.12 ) e infine repositery (mavenCentral()). Le configurazioni sono un elemento chiave in Gradle, rappresentano un gruppo di dipendenze, che si differenzia l’uno dall’al-tro in base a diversi fattori, come la fase della build nella quale si applicano al progetto, o la possibilit`a di accedervi all’utente finale o meno.

Listato 2.9: Esempio di configurazione delle dipendenze d e p e n d e n c i e s { api (" com . a n d r o i d . t o o l s . b u i l d : g r a d l e : 3 . 5 . 1 ") i m p l e m e n t a t i o n (" com . a n d r o i d . t o o l s . b u i l d : g r a d l e : 3 . 5 . 1 ") t e s t I m p l e m e n t a t i o n (" com . a n d r o i d . t o o l s . b u i l d : g r a d l e : 3 . 5 . 1 ") }

(29)

2.3. AUTOMAZIONE DEL PROGETTO 15 r e p o s i t o r i e s {

m a v e n C e n t r a l () m a v e n {

url = uri (" h t t p s :// r e p o s i t o r y - cdn . l i f e r a y . com / n e x u s / c o n t e n t / g r o u p s / p u b l i c ")

} }

Possono essere costruite ad-hoc dal programmatore, ma generalmente vengono introdotte attraverso plugin5, come quelle pi`u basilari introdotte con Java plugin:

• implementation • api • compileOnly • runtimeOnly • testImplementation • testCompileOnly • testRuntimeOnly

Il primo pu`o essere considerato quello di default, le dipendenze in questione sono disponibili in tutte le fasi della build, ma non sono esposte all’utente finale, quindi non verranno aggiunte al classpath compilato. Nell’aggiornamento a Gradle 3.0 come annunciato da Google al I/O ’17 (Speeding Up Your Android Gradle Builds (Google I/O ’17)), implementation ha sostituito compile. Api, `e del tutto uguale a implementation, tranne per il motivo che aggiunge transitivit`a alla dipendenza, quindi la rende accessibile anche al cliente finale del progetto. Nel caso in cui il programmatore volesse limitare l’uso di un modulo a tempo di compilazione o a

5Elemento che permette di estendere le funzionalit`a di Gradle, sar`a oggetto di

(30)

runtime, esistono le configurazioni rispettive, compileOnly e runtimeOnly. Rical-cando la logica di implementation, testImplementation rende i moduli accessibili soltanto durante la compilazione e durante il tempo d’esecuzione dei test. testCom-pileOnly e testRuntimeOnly invece espongono i moduli rispettivamente durante la fase di compilazione o di runtime dei test.

Nella terminologia di Gradle, le librerie esterne, generalmente distribuite sottofor-ma di JAR, vengono denominate moduli, e sono identificabili attraverso un vettore di tre parametri: group, name e version. Riferendosi al listato 2.9, e analizzando quindi la libreria gradle, group identifica la compagnia, il progetto o l’organizza-zione che detiene il modulo (”com.android.tools.build”), mentre name identifica la libreria rispetto alle altre (”gradle”), mentre version identifica quale release `e oggetto di dipendenza (3.5.1). A volte, le librerie utili al progetto in questione pos-sono anche non essere distribuite in appositi repository online, indipendentemente dal motivo questo strumento mette a disposizione al programmatore una modo di gestire queste dipendenze. Questo modo di operare, `e generalemente sconsigliato, perch`e al crescere di questo tipo di dipendenze aumenta anche il tempo impiegato nella gestione e nella manutenzione delle stesse, tuttavia una buona pratica `e riu-nire le librerie interessate in una cartella ”lib”, e di richiamarle complessivamente (fileTree) o singolarmente (files) come nel listato 2.10.

Listato 2.10: Dipendenze da file locali d e p e n d e n c i e s { i m p l e m e n t a t i o n ( f i l e T r e e ( m a p O f (" dir " to " libs ", " i n c l u d e " to l i s t O f (" *. jar ") ) // 1 ) ) i m p l e m e n t a t i o n (

f i l e s (" $ p r o j e c t D i r / lib /3 r d p a r t y / gson - 2 . 8 . 5 . jar ")

// 2

(31)

2.3. AUTOMAZIONE DEL PROGETTO 17 }

Gradle non gestisce solamente dipendenze esterne, ma anche interne, infatti in una build multi-progetto, `e possibile che un progetto sia dipendente da un altro. Come rappresentato nel listato 2.11, il project app `e dipendente dal subproject http, dal task ”install” del subproject react e dagli artefatti prodotti durante la configurazione ”pubblished” del subproject react.

Listato 2.11: Dipendenze tra progetti p r o j e c t (" : app ") { d e p e n d e n c i e s { " i m p l e m e n t a t i o n "( p r o j e c t (" : http ") ) " i m p l e m e n t a t i o n "( p r o j e c t (" : r e a c t : i n s t a l l ") ) // d e p e n d e n c y of s u b p r o j e c t ’ s task " i m p l e m e n t a t i o n "( p r o j e c t ( path = " : api ", c o n f i g u r a t i o n = " p u b b l i s h e d ") ) // d e p e n d e n c y // of s p e c i f i c c o n f i g u r a t i o n ’ s u b p r o j e c t } }

In conclusione, nella maggiore parte dei casi le dipendenze sono moduli esterni vincolati su appositi repository online, ogni progetto detiene una lista di que-sti, e durante la build, Gradle risolve le dipendenze caricando in cache i moduli opportuni. Esistono tre tipi di repository:

• Maven • Ivy

• cartelle statiche

Nei prossimi paragrafi, i tre tipi saranno analizzati e supportati di configurazione in Kotlin DSL.

(32)

Maven `e il tipo di repository pi`u diffuso (Gradle in Action), il modulo `e formato dalla libreria sotto-forma di JAR, e di un file di configurazione XML, detto POM. Questo file contiene informazioni rilevanti sulla libreria e in particolare sulla sua transitivit`a. Entrambi gli artefatti vengono localizzati sul repository grazie agli attributi dichiarati nella build script, in particolare la notazione dot di group indica il percorso specifico sul repository. Tra i pi`u considerevoli di questo tipo troviamo Maven Central (Berglund, 2013), non solo per la variet`a di librerie che ospita, ma anche per la sua accessibilit`a facilitata rispetto ai repository Maven ospitati su reti private. Infatti come si denota nel listato 2.12, Gradle offre supporto diretto per Maven Central, permettendo di omettere il suo url (http://repo1.maven. org/maven2), seguono anche le configurazioni per aggiungere un repository con url specifico e il Maven repository locale ( /.m2 )

(33)

2.3. AUTOMAZIONE DEL PROGETTO 19 Listato 2.12: Dichiarazione dei repository Maven

r e p o s i t o r i e s {

m a v e n C e n t r a l () // http :// r e p o 1 . m a v e n . org / m a v e n 2

m a v e n L o c a l ()

m a v e n ( url = " < M A V E N REPO URL > ") }

Se la dichiarazione degli artefatti in Maven seguono un layout preciso, previa l’impossibilit`a di risolvere le dipendenze, in Ivy il layout pu`o essere stabilito a pia-cimento, infatti la sua struttura pu`o essere profondamente diversa. Generalmente Gradle assume che la struttura sia uguale a quella di Maven, nel caso si voles-se modificare il pattern di dichiarazione degli artefatti si pu`o usare la funzione ”patternLayout”, come mostrato nel listato 2.13.

Listato 2.13: Dichiarazione dei repositery Ivy r e p o s i t o r i e s {

ivy {

url = uri (" http :// repo . m y c o m p a n y . com / repo ") p a t t e r n L a y o u t { a r t i f a c t (" 3 rd - party - a r t i f a c t s / [ o r g a n i s a t i o n ]/[ m o d u l e ]/ [ r e v i s i o n ]/[ a r t i f a c t ] -[ r e v i s i o n ].[ ext ] " ) a r t i f a c t (" company - a r t i f a c t s / [ o r g a n i s a t i o n ]/[ m o d u l e ]/ [ r e v i s i o n ]/[ a r t i f a c t ] -[ r e v i s i o n ].[ ext ] " ) ivy (" ivy - f i l e s / [ o r g a n i s a t i o n ]/[ m o d u l e ]/ [ r e v i s i o n ]/ ivy . xml " ) }

(34)

} }

Gralde non solo supporta una dipendenza locale, ma permette di dichiarare anche repository locali, questa funzionalit`a `e utile quando un programmatore vuole usare un proprio archivio di librerie, tuttavia `e una pratica sconsigliata perch`e Gralde non si prende carico nella gestione delle dipendenze transitive. In seguito, le dipendenze locali possono essere dichiarate soltanto per nome e versione come nel listato 2.14.

Listato 2.14: Dichiarazione repository locale r e p o s i t o r i e s {

f l a t D i r {

dirs (" lib ") }

f l a t D i r {

dirs (" lib1 ", " lib2 ") }

}

Per completare il concetto di repository, Gradle pu`o dichiarare anche archivi pro-tetti da password (es. repository aziendali o di organizzazioni) con il metodo ”credentials”, che permette di aggiungere un nome utente e un password di lo-gin. Rimane buona pratica, tuttavia, non dichiarare le credenziali nel build script, ma dichiararle nel file di configurazione ”gradle.properties” e di adoperarle come mostrato nel listato 2.15.

Listato 2.15: Autenticazione tramite credenziali

// s e t t i n g s . g r a d l e

u s e r n a m e = " m y u s e r n a m e "

p a s s w o r d = " m y p a s s w o r d "

// g r a d l e . b u i l d . kts

(35)

2.3. AUTOMAZIONE DEL PROGETTO 21 m a v e n {

url = uri (" http :// repo . m y c o m p a n y . com / m a v e n 2 ") c r e d e n t i a l s { u s e r n a m e = u s e r n a m e p a s s w o r d = p a s s w o r d } } }

In conclusione, Gradle si dimostra uno strumento avanzato nella gestione delle dipendenze rispetto alla concorrenza (Gradle in Action), infatti offre una perfor-mante gestione dei moduli attraverso la cache, memorizzando separatamente in locale i moduli e i loro metadata (pom.xml e ivy.xml ). Mentre i secondi sono identificati dalla tupla group ID, artifact ID, versione e repository di provenienza, i secondi sono classificati in base alla funzione crittografica SHA-1. Distaccando l’informazione del repository di provenienza, l’artefatto diventa localmente uni-versale e indipendente, permettendo a Gradle di scaricare una sola copia della libreria, e non pi`u versioni della stessa per ogni repository dichiarato. In questo modo, se il modulo non `e gi`a presente in cache, questo strumento tenta di scarica-re quello con lo SHA di grandezza minoscarica-re, ottimizzando i tempi di download e di build. Esistono una coppia di comandi per modificare il comportamento di Gradle,

--offline forza l’uso della cache nel risolvere le dipendenze, permettendo

di lavorare senza nessuna connessione, mentre --refresh-dependencies in

contrasto, aggiorna i meta-data utilizzando la connessione internet. Per comple-tare i concetti precedenti, `e fondamentale per il programmatore comprendere la strategia applicata da questo strumento nella gestione dei conflitti tra le dipenden-ze. Spesso, data la transitivit`a dei moduli, pu`o essere necessario la stessa libreria ma di versioni differenti, Gradle appunto di default scarica la versione pi`u recen-te, almeno che il programmatore non abbia dichiarato nel build script la versione specifica da preferire.

(36)

c o n f i g u r a t i o n s . all { r e s o l u t i o n S t r a t e g y . e a c h D e p e n d e n c y { if ( r e q u e s t e d . g r o u p == " org . g r a d l e ") { u s e V e r s i o n (" 1.4 ") b e c a u s e (" API b r e a k a g e in h i g h e r v e r s i o n s ") } } }

2.3.3

Estensione delle funzionalit`

a di Gradle

Gradle offre diversi approcci, con i suoi pro e i suoi contro, per riusare il codice, uno tra questi `e l’uso di plugin. Questo elemento che `e gi`a stato introdotto durante lo studio delle dipendenze, in particolari nelle configurazioni per Java, permette di estendere le funzionalit`a di Gradle in tre modi differenti. Il primo, modifica l’oggetto Project, arricchendo la build corrente con tasks, SourceSets, dependencies e repositories. Il secondo modo, pu`o introdurre uno o pi`u moduli per delegare complessi lavori a librerie esterne. Infine, il terzo modo, decisamente fondamentale per facilitare l’uso del plugin, `e la possibilit`a di introdurre nuove keyword e domain object nel linguaggio di compilazione di Gradle. Questo `e necessario, perch´e i DSL come analizzato nella sezione 2.2, non possono per definizione essere adatti ad ogni scenario e dominio nel quale il progetto pu`o andare in contro. In Gradle, esistono due tipi di plugin, script plugins e binary plugins. I primi sono essenzialmente composti da build script che si aggiungono alla build corrente e implementano un approccio dichiarativo nel manipolare il progetto, si differenziano dai secondi, che invece estendono l’interfaccia Gradle Plugin e adottano un approccio pragmatico nella manipolazione della build. Per usare la logica di un Plugin, Gradle come nelle dipendenze, deve risolverlo, ovvero identificare la giusta versione del jar che lo contiene e aggiungerlo al classpath, in seguito, generalmente viene gi`a applicato al target (generalmente un Project ) attraverso la funzione Plugin.apply(T),

(37)

2.3. AUTOMAZIONE DEL PROGETTO 23 o come consiglia Gradle stesso (Using Gradle Plugins) attraverso il plugins DSL, come mostrato nel listato 2.17.

Listato 2.17: Diversi modi di applicare un plugin

a p p l y ( from = " o t h e r . g r a d l e . kts ") // a p p l y s c r i p t p l u g i n p l u g i n s { java // a p p l y core p l u g i n // a p p l y c o m m u n i t y p l u g i n id (" com . j f r o g . b i n t r a y ") v e r s i o n " 0 . 4 . 1 " }

In conclusione, Gradle `e uno strumento che permette non soltanto di ge-stire il codice in pi`u aspetti, ma anche di cambiare profondamente la natura di un progetto, soprattutto tramite i plugin. Questi elementi, potenziano que-sto strumento ulteriormente, infatti nei prossimi capitoli, sulla base del plugin ”kotlin-multiplatform”, verr`a analizzata la costruzione di un applicazione mul-tipiattaforma, e in particolare come si organizza il codice e come si scrive il build-script.

(38)
(39)

Capitolo 3

Applicazioni multipiattaforma

Con il rilascio di Kotlin 1.1 nel 2013, JetBrains ha rilasciato Koltin JS, permettendo di compilare, appunto, Kotlin in Javascript (Belov, 2017). Se prima i program-matori attraverso questo linguaggio, avevano la possibilit`a di modellare la logica di un applicativo e poi tradurlo in Java, con questo aggiornamento ora `e possibile modellare anche il front-end e compilarlo quindi con Javascript. Pi`u avanti, alla fine del 2017, Jetbrains `e andato oltre, rilasciando Kotlin Multiplatform (Jemerov, 2017), questa funzione , ancora sperimentale, permette di condividere il codice attraverso qualsiasi client JS o JVM based, come un frontend in React, un client Android, un client Desktop, e molto altro.

L’idea principale di un progetto multipiattaforma `e condividere il codice tra di-versi team di sviluppo e piattaforme. Se da una parte questa prerogativa, nata in concomitanza con la stesura del codice, `e diventata l’obiettivo di moltissimi progetti odierni (Tobias Heine, 2018), organizzare il codice in modo ordinato e ottimizzato tra pi`u piattaforme rimane una sfida ancora aperta. Le difficolt`a nate con il crescere e il diversificarsi delle tecnologie, hanno portato alla nascita di di-versi tool come PhoneGap, Titanium, Xamarin, Ionic, RubyMotion e molti altri. Questi strumenti si sono rilevati nel corso del tempo, poco flessibili e poco adatti a coprire i diversi SDK, e le esigenze delle varie piattaforme, soprattutto per quan-to concerne UX e UI dell’applicativo, portando inevitabilmente gli sviluppaquan-tori a diversificare il team in progetti differenti (touchlab, 2019; Tobias Heine, 2018).

(40)

Figura 3.1: Distribuzione di Kotlin su pi`u piattaforme JS/JVM based

Kotlin si differenzia dai suoi concorrenti perch´e non traduce tutto il codice in quel-lo specifico della piattaforma, ma lascia alquel-lo sviluppatore pieni poteri nel decidere quali parti del codice rendere condivise e quali native. Generalmente, soprattutto per le piattaforme front-end, i progetti multipiattaforma mirano a condividere la logica e a lasciare implementare le parti critiche agli SDK, in modo da sviluppare un’unica logica robusta di business per ogni piattaforma, abbattendo quindi anche i costi e i tempi di produzione.

3.1

Logica di base

Il progetto `e organizzato in moduli che condividono pi`u o meno diverse parti di codice, la logica alla base della divisione in moduli segue tre blocchi fondamentali: • Target, `e la parte di build responsabile alla costruzione, testing, e packaging di una parte di software per una delle piattaforme. Generalmente, seguono pi`u target in un progetto multipiattaforma.

(41)

3.1. LOGICA DI BASE 27 • Compilazioni, la finalizzazione in target del codice segue una o pi`u fasi di compilazione del sorgente. Pu`o esistere un tipo di compilazione per la produzione del sorgente principale, e uno per i test.

• Source sets, sono l’insieme di cartelle che contengono i sorgenti del codice in Kotlin o nel linguaggio specifico della piattaforma. Raggruppano con s’`e anche le dipendenze specifiche per la piattaforma e possono avere relazioni di dipendenza tra loro.

Per ogni compilazione esiste, di default, un suo source set, che in un applicazione multipiattaforma generale viene utilizzato anche per indirizzare altri set di sorgen-ti alla compilazione, mediante la relazione ”dipende da” come mostrato nella fig. 3.2. Il layout in questione viene ottenuto di default attraverso la creazione di due

Figura 3.2: Per ogni target, due compilazioni di default, main e test, e i relativi source set anch’essi di base

target jvm e js senza nessuna configurazione aggiuntiva. In particolare, ”jvmMain” condivide il sorgente con ”commonMain”, che attraverso la compilazione ”main”

(42)

riescono a soddisfare la richiesta del target specifico jvm.

Nel concreto, questa relazione di dipendenza si basa sul meccanismo ”actual/ex-pect”, una tecnica avanzata che permette di gestire le specificit`a delle piattaforme e che `e oggetto di approfondimento nei prossimi paragrafi.

3.1.1

Meccanismo Expect/Actual

Kotlin `e uno strumento importante nell’applicazioni multipiattaforma, perch´e co-me `e gi`a stato accennato, permette di organizzare il codice tra pi`u piattaforme in modo coeso e organizzato, permettendo allo sviluppatore di accedere anche alle funzioni specifiche di una piattaforma. Una delle funzionalit`a chiave del codice multipiattaforma di questo linguaggio `e il modo in cui il sorgente comune dipende dalle dichiarazioni specifiche della piattaforma, che tecnicamente si realizza nella costruzione di una serie di interfacce (expect declaration, appunto le dichiarazione che il codice comune si aspetta) che verranno poi implementate in modo adeguato per ogni piattaforma (actual declaration).

Un esempio concreto nell’uso di questo meccanismo pu`o essere la costruzione di un framework di logging minimalista, il listato 3.1 mostra il codice in comune tra le piattaforme e l’uso della keyword expect

Listato 3.1: Esempio di uso expect nel codice comune

// C o m m o n Code

enum c l a s s L o g L e v e l { // c o m p i l e d for all p l a t f o r m

DEBUG , WARN , E R R O R }

// e x p e c t e d API

i n t e r n a l e x p e c t fun w r i t e L o g M e s s a g e ( m e s s a g e : String, l o g L e v e l : L o g L e v e l )

(43)

3.1. LOGICA DI BASE 29

// e x p e c t e d API c o u l d be used in c o m m o n code

fun l o g D e b u g ( m e s s a g e : S t r i n g) = w r i t e L o g M e s s a g e ( message , L o g L e v e l . D E B U G ) fun l o g W a r n ( m e s s a g e : S t r i n g) = w r i t e L o g M e s s a g e ( message , L o g L e v e l . WARN ) fun l o g E r r o r ( m e s s a g e : S t r i n g) = w r i t e L o g M e s s a g e ( message , L o g L e v e l . E R R O R )

che `e limitato in questo caso alle funzioni, ma pu`o essere adoperata su qualsiasi di-chiarazione in Kotlin, come classi astratte, interfacce e pi`u in generale qualsiasi fun-zione top-level (anche le annotazioni). In questo modo, writeLogMessage()

pu`o essere usato nel sorgente comune indipendentemente dalla sua implementazio-ne, che verr`a concretizzata correttamente nei moduli specifici, come mostrato in questo caso specifico nel listato 3.2.

Listato 3.2: Implementazione di writeLogMessage su ogni piattaforma specifica

// JVM i n t e r n a l a c t u a l fun w r i t e L o g M e s s a g e ( m e s s a g e : String, l o g L e v e l : L o g L e v e l ) { p r i n t l n (" [ $ l o g L e v e l ]: $ m e s s a g e ") } // JS i n t e r n a l a c t u a l fun w r i t e L o g M e s s a g e ( m e s s a g e : String, l o g L e v e l : L o g L e v e l ) { when ( l o g L e v e l ) { L o g L e v e l . D E B U G -> c o n s o l e . log ( m e s s a g e ) L o g L e v e l . WARN -> c o n s o l e . warn ( m e s s a g e ) L o g L e v e l . E R R O R -> c o n s o l e . e r r o r ( m e s s a g e )

(44)

} }

In conclusione, durante la compilazione, il compilatore si assicura che tutte le di-chiarazioni actual e expect coincidano, per questo le dichiarazione correlate devono coincidere nel nome completo.

3.2

Setup di un progetto multipiattaforma con

Gradle

JetBrains, azienda sviluppatrice di Kotlin, nel suo IDE IntelliJ IDEA, attraverso il plugin Kotlin (aggiornato alla versione 1.2), ha messo a disposizione degli svi-luppatori un wizard guidato per la creazione di progetti multipiattaforma. Per iniziare la configurazione `e necessario Gradle aggiornato almeno alla versione 4.7, che a procedura terminata applica il plugin ”kotlin-multiplatform” permettendo al programmatore di accedere a tutte le funzionalit`a di Kotlin Multipiattaforma (configurazione dei target, source sets, dipendenze e altro).

Listato 3.3: kotlin-multiplatform plugin p l u g i n s {

k o t l i n (" m u l t i p l a t f o r m ") v e r s i o n " 1 . 3 . 5 0 "

}

A configurazione completata, il progetto dispone tre target principali, JVM, JS e uno per la piattaforma nativa in uso, questi vengono configurati come nel li-stato 3.4 mediante l’uso delle funzioni predefinite: jvm(), js() e

mingwX64() nel caso l’host sia Windows64.

Listato 3.4: Configurazione dei target nel file gradle.build.kts p l u g i n s {

k o t l i n (" m u l t i p l a t f o r m ") v e r s i o n " 1 . 3 . 5 0 "

(45)

3.2. SETUP DI UN PROGETTO MULTIPIATTAFORMA CON GRADLE 31 r e p o s i t o r i e s {

m a v e n C e n t r a l () }

k o t l i n {

jvm () // C r e a t e s a JVM t a r g e t with the d e f a u l t name ’ jvm ’ js () // JS t a r g e t n a m e d ’ js ’ m i n g w X 6 4 (" m i n g w ") // W i n d o w s ( M i n G W X64 ) t a r g e t n a m e d ’ mingw ’ s o u r c e S e t s { /* ... */ } }

Tuttavia queste non sono le uniche piattaforme supportate, ma l’elenco delle funzioni prestabilite si espande alle seguenti piattaforme:

• android per librerie e applicazioni Android;

• androidNativeArm32 e androidNativeArm64 per Android NDK;

• iosArm32, iosArm64e iosX64 per iOS;

• linuxArm32Hfp, linuxMips32, linuxMipsel32, linuxX64per Linux;

• macosX64 per MacOs;

• wasm32 per WebAssembly;

• watchosArm32, watchosArm64, watchosX86 per watchOs;

• tvosArm64,tvosX64 per tvOs.

Di default, ogni progetto contiene due source sets, ”commonMain” e ”common-Test” collegati alle rispettive configurazioni ”main” e ”test”, questi due moduli contengono tutto il sorgente condiviso dalle piattaforme. Oltre a questi due mo-duli, per ogni target dichiarato, seguono due compilazioni ”main” e ”test” e la serie

(46)

di source sets, che prendono il nome in base alla compilazione e al target in questio-ne; questi moduli che seguono quindi la dicitura ¡nomeTarget¿¡nomeCompilazione¿ di default contengono il codice specifico per quella piattaforma e partecipano alla compilazione del target.

In conclusione in un progetto multipiattaforma su JS, JVM, i moduli di base saranno ”commonMain” e ”commonTest” per il sorgente comune, ”jvmMain” e ”jvmTest” per JVM, infine ”jsMain” e ”jsTest” per JS. Queste diciture nascondo-no, senza nessun tipo di configurazione in Gradle, delle relazioni di dipendenza, infatti `e evidente che nella costruzione del target vengano compilati sia i sorgenti comuni che quelli del source set specifico.

(47)

3.2. SETUP DI UN PROGETTO MULTIPIATTAFORMA CON GRADLE 33

3.2.1

Configurazione dei Target

Per Target si intende quella parte di build responsabile alla compilazione, test e packaging di una porzione di software adibita ad una piattaforma specifica. Queste porzioni possono contenere sia del codice comune che quello specifico della piatta-forma, tecnicamente i target vengono configurati attraverso le funzioni predefinite gi`a accennate nei paragrafi precedenti, queste opzionalmente, accettano il nome del target e una codice per la configurazione dello stesso, come mostrato nel listato 3.5

Listato 3.5: Configurazioni dei target con le funzioni prestabilite di Kotlin k o t l i n {

jvm () { // C r e a t e a JVM t a r g e t with the d e f a u l t name ’ jvm ’ w i t h J a v a () } jvm (" j v m L i b r a r y ") js (" n o d e J s ") // C r e a t e a JS t a r g e t with a c u s t o m name ’ nodeJs ’ l i n u x X 6 4 (" l i n u x ") {

/* S p e c i f y a d d i t i o n a l s e t t i n g s for the ’ linux ’ t a r g e t here */

} }

Ogni target creato viene aggiunto alla collezione kotlin.targetsche

permet-te di accedere globalmenpermet-te ad ognuno di loro, o singolarmenpermet-te attraverso il nome; se invece si vuole creare pi`u target contemporaneamente o configurarli si pu`o adope-rare la notazione targetFromPreset() che accetta come argomento un preset

(contenuti in kotlin.presets) e un blocco di codice configurativo per i target

interessati. Da Kotlin 1.3.40 il target predefinito jvm supporta anche i sorgenti Java attraverso il relativo plugin, applicabile con la funzione withJava().

(48)

Listato 3.6: Diversi modi di accedere ai target creati i m p o r t org . j e t b r a i n s . k o t l i n . g r a d l e . p l u g i n . mpp . K o t l i n N a t i v e T a r g e t P r e s e t k o t l i n { t a r g e t s . all { c o m p i l a t i o n s [" main "]. d e f a u l t S o u r c e S e t { /* ... */ } } p r e s e t s . withType < K o t l i n N a t i v e T a r g e t P r e s e t >() . f o r E a c h { t a r g e t F r o m P r e s e t (it) { /* C o n f i g u r e each of the c r e a t e d t a r g e t s */ } } }

3.2.2

Configurazione dei Source Sets

Un source set `e una collezione di sorgenti Kotlin, risorse e dipendenze che parte-cipano alla compilazione di uno o pi`u target, il suo contenuto dipende dalla sua applicazione, infatti se partecipa a pi`u compilazioni, il suo codice dipende solamen-te dalla libreria comune di Kotlin (kotlin-stdlib-common), mentre se parsolamen-tecipa alla costruzione di un singolo target, pu`o usufruire delle sue specificit`a sia per le dipen-denze che per il codice. I source set predefiniti di Kotlin, come ”commonMain” e ”commonTest” sono gi`a configurati, per modificarli o aggiungerne di nuovi si pu`o usare la notazione sourceSets ... come mostrato nel listato 3.7

Listato 3.7: Configurazione del build.file per creare e modificare un source set esistente

(49)

3.2. SETUP DI UN PROGETTO MULTIPIATTAFORMA CON GRADLE 35 s o u r c e S e t s {

val foo by c r e a t i n g { /* ... */ } // crea un n u o v o s o u r c e set con il nome " foo "

val c o m m o n M a i n by g e t t i n g { // c o n f i g u r a un s o u r c e set e s i s t e n t e e m o d i f i c a il nome d e l l e c a r t e l l e c o n t e n e n t i il s o r g e n t e e le r i s o r s e k o t l i n . s r c D i r (" src ") r e s o u r c e s . s r c D i r (" res ") } } }

Generalmente i source sets sono creati mediante le funzioni predefinite, ma ov-viamente Kotlin non vieta la creazione di set personalizzati anche se questi non portano con s´e nessun tipo di configurazione, infatti per partecipare alla costruzio-ne di uno specifico target devono essere dipendenti dalla compilaziocostruzio-ne di un source set predefinito.

Supponendo che un ipotetico set ”foo” dipenda da un secondo ipotetico set ”bar”, le relazioni di dipendenza si articolano su 5 punti:

• Nel momento in cui ”foo” partecipa nella compilazione di un determinato target, ”bar” prende parte a quella compilazione, producendo gli stessi binari; • ”foo” pu`o accedere alle dichiarazioni di ”bar”, nonostante siano dichiarate con ”internal”, e alle sue dipendenze, anche se specificate come ”implemen-tation”;

• ”foo” pu`o implementare le dichiarazioni expect contenute in ”bar”;

• le risorse di ”bar” vengono processate e copiate sempre insieme a quelle di ”foo”;

• i settaggi sul linguaggio adoperato in ”foo” e in ”bar” devono essere consi-stenti.

(50)

Le relazioni di dipendenza si esprimono con la notazione dependsOn(), e

co-me riportato nel docuco-mento ufficiale Building Multiplatform Projects with Gradle, nella maggior parte dei casi, i set personalizzati seguono la configurazione mostrata nel listato 3.8.

Listato 3.8: Target personalizzati e relazioni di dipendenza k o t l i n {

m i n g w X 6 4 () l i n u x X 6 4 ()

s o u r c e S e t s {

// c u s t o m s o u r c e set with t e s t s for the two t a r g e t s

val d e s k t o p T e s t by c r e a t i n g {

d e p e n d s O n ( g e t B y N a m e (" c o m m o n T e s t ") )

/* ... */

}

// Make the ’ windows ’ d e f a u l t test s o u r c e set for d e p e n d on ’ d e s k t o p T e s t ’ m i n g w X 6 4 () . c o m p i l a t i o n s [" test "]. d e f a u l t S o u r c e S e t { d e p e n d s O n ( d e s k t o p T e s t ) /* ... */ }

// And do the same for the o t h e r t a r g e t :

l i n u x X 6 4 () . c o m p i l a t i o n s [" test "]. d e f a u l t S o u r c e S e t { d e p e n d s O n ( d e s k t o p T e s t ) /* ... */ } } }

(51)

3.2. SETUP DI UN PROGETTO MULTIPIATTAFORMA CON GRADLE 37 Kotlin non esclude l’utilizzo di moduli esterni al progetto e gestisce tramite Gradle le dipendenze esterne con la funzione dependencies ... appartenente al

DSL di source sets.

Sono quattro i tipi di dipendenze supportati:

• ”api”, il modulo in questione pu`o essere utilizzato sia durante in fase di compilazione che di runtime, inoltre `e accessibile all’utente finale;

• ”implementation”, la libreria esterna `e accessibile nelle stesse fasi build di ”api” ma si differenzia da quest’ultimo non esponendo il modulo al consu-matore finale;

• ”compileOnly”, il modulo in questione partecipa solamente alla sua fase di compilazione, e non rimane visibile in nessuna fase di build degli altri moduli; • ”runtimeOnly”, segue la stessa logica della dipendenza precedente rimanendo invisibile durante la compilazione di altri moduli, tuttavia si differisce usando la libreria esterna in questione durante la fase di runtime;

Listato 3.9: Esempio di configurazione delle dipendenze dei source set ”commonMain” e ”jsMain” k o t l i n { s o u r c e S e t s { val c o m m o n M a i n by g e t t i n g { d e p e n d e n c i e s { i m p l e m e n t a t i o n ( k o t l i n (" stdlib - c o m m o n ") ) api ( p r o j e c t (" : foo - lib ") ) // p r o j e c t

d e p e n d e n c y

} }

val j s M a i n by g e t t i n g { d e p e n d e n c i e s {

(52)

} } } }

Come mostrato nel listato 3.9, Kotlin Multipiattaforma supporta anche le di-pendenze di progetti esterni, infatti nel listato in questione, la compilazione che adoperer`a il source set ”commonMain” ricever`a una versione specifica dell’artefatto del progetto, risolvendola per quel target specifico.

Listato 3.10: Dichiarazione alternativa delle dipendenze attraverso la funzione top-level di Gradle DSL

d e p e n d e n c i e s {

" c o m m o n M a i n A p i "(" com . e x a m p l e : foo - c o m m o n :1.0 ")

" j v m 6 M a i n A p i "(" com . e x a m p l e : foo - jvm6 :1.0 ") }

Il linguaggio contenuto nei source set pu`o anch’esso subire diverse configura-zioni attraverso la funzione languageSettings, che permette di modificare

globalmente o singolarmente l’analisi dei sorgenti da parte dell’IDE. L’unico limite posto riguarda le dipendenze tra i set, infatti se un ipotetico ”foo” dipende da ”bar”:

• ”foo” deve possedere una versione di linguaggio superiore a quella di ”bar”; • ”foo” deve abilitare tutti le funzioni di linguaggio instabili che ”bar” ha

abilitato;

• ”foo” deve usare tutte le annotazioni sperimentali usate da ”bar”.

Listato 3.11: Impostazioni relativi ai sorgenti k o t l i n {

s o u r c e S e t s {

(53)

3.2. SETUP DI UN PROGETTO MULTIPIATTAFORMA CON GRADLE 39 l a n g u a g e S e t t i n g s . a p p l y { l a n g u a g e V e r s i o n = " 1.3 " // p o s s i b l e v a l u e s : ’1.0 ’ , ’1.1 ’ , ’1.2 ’ , ’1.3 ’ a p i V e r s i o n = " 1.3 " // p o s s i b l e v a l u e s : ’1.0 ’ , ’1.1 ’ , ’1.2 ’ , ’1.3 ’ e n a b l e L a n g u a g e F e a t u r e (" I n l i n e C l a s s e s ") // l a n g u a g e f e a t u r e name u s e E x p e r i m e n t a l A n n o t a t i o n (" k o t l i n . E x p e r i m e n t a l U n s i g n e d T y p e s ") // a n n o t a t i o n FQ - name p r o g r e s s i v e M o d e = true // f a l s e by d e f a u l t } } } } // M o d i f i c a r e t u t t i i s o r g e n t i di t u t t i i s o r u c e sets k o t l i n . s o u r c e S e t s . all { l a n g u a g e S e t t i n g s . p r o g r e s s i v e M o d e = true }

3.2.3

Criticit`

a e Interoperabilit`

a in JVM e JS

Kotlin per natura si differenzia da Java e da JavaScript sotto diversi aspetti, in primis il concetto di ”static” in Java `e gestito attraverso gli oggetti, mentre rispetto a JavaScript, sempre Kotlin, si presenta per natura come un linguaggio fortemente tipizzato e non dinamico.

La staticit`a infatti rispetto al linguaggio di Oracle `e modellata come un ogget-to di una classe, pi`u precisamente come singleton, e se seguita dalla

(54)

notazio-ne ”companion”, come mostrato notazio-nel listato 3.12, non deve essere istanziata per accedervi.

Listato 3.12: Companion Object essendo un oggetto pu`o essere esteso o implementare delle interfacce

c l a s s C { c o m p a n i o n o b j e c t { @ J v m S t a t i c fun c a l l S t a t i c () {} fun c a l l N o n S t a t i c () {} } } // in Java C . c a l l S t a t i c () ; // w o r k s fine C . c a l l N o n S t a t i c () ; // e r r o r : not a s t a t i c m e t h o d C . C o m p a n i o n . c a l l S t a t i c () ; // i n s t a n c e m e t h o d r e m a i n s

C . C o m p a n i o n . c a l l N o n S t a t i c () ; // the only way it w o r k s

Durante la traduzione del codice in Java `e utile apportare l’annotazione @JvmStatic,

in questo modo il compilatore generer`a sia un metodo statico che un metodo istan-ziato nella classe corrispondente.

A differenza di Kotlin e anche Java in questo caso, Javascript non supporta l’o-verload dei metodi e soprattutto non ha variabili tipizzate, ma solo dinamiche. Questa criticit`a `e stata risolta dagli sviluppatori di JetBrains attraverso l’introdu-zione della notal’introdu-zione ”dynamic”; principalmente una variabile dichiarata in questo modo segue la logica di una variabile Javascript rimanendo libera da qualsiasi ti-po. A livello tecnico, il compilatore traduce queste espressioni cos`ı come sono, non apportando nessun controllo (null-check disabilitati).

Per quanto concerne l’overload dei metodi, in Kotlin le funzioni interessate van-no diversificate mediante l’anvan-notazione @JsName che accetta come stringa il

(55)

3.2. SETUP DI UN PROGETTO MULTIPIATTAFORMA CON GRADLE 41 Listato 3.13: Uso di @JsName()

// M o d u l e ’ kjs ’ c l a s s P e r s o n (val name : S t r i n g) { fun h e l l o () { p r i n t l n (" H e l l o $ n a m e ! ") } @ J s N a m e (" h e l l o W i t h G r e e t i n g ") fun h e l l o ( g r e e t i n g : S t r i n g) { p r i n t l n (" $ g r e e t i n g $ n a m e ! ") } } // J a v a s c r i p t E c o s y s t e m var p e r s o n = new kjs . P e r s o n (" U m b e r t o ") ; // r e f e r s to m o d u l e ’ kjs ’ p e r s o n . h e l l o () ; // p r i n t s " H e l l o U m b e r t o !" p e r s o n . h e l l o W i t h G r e e t i n g (" S e r v u s ") ; // p r i n t s " S e r v u s U m b e r t o !"

In conclusione, costruire un applicazione multipiattaforma attraverso Gradle e Kotlin `e sicuramente pi`u veloce grazie alle configurazioni predefinite del plugin ”koltin-multiplatform”, questa nuova funzione di Kotlin `e ancora in fase speri-mentale, tuttavia JetBrains sta investendo molto nel suo sviluppo visto i repentini aggiornamenti apportati. In questo capitolo `e stata introdotta la logica di base, e sono stati approfonditi gli elementi che stanno alla base di questa tecnologia, come i target e i source set, mostrando valide configurazioni DSL. Tuttavia, non tutti gli argomenti sono stati sviscerati, nei prossimi capitoli l’attenzione si porr`a sulla fase di testing e deploy.

(56)
(57)

Capitolo 4

Test e Deploy di una libreria

multipiattaforma

Una visione ingegneristica non esclude un’applicazione multipiattaforma dalla fase di collaudo prima del deploy. Queste operazioni, che sono pi`u complesse vista le diversit`a dei sorgenti, sono pienamente supportate da Gradle. Nel corso di questo capitolo analizzeremo il supporto specifico offerto da questo strumento, supportando la teoria con numerosi esempi di codice, in modo da dare continuit`a al ruolo guida di questo documento.

4.1

Testing

Il testing dei sorgenti `e una procedura atta ad individuare le carenze di correttez-za, completezza e affidabilit`a di un prodotto software in corso di sviluppo, il suo obbiettivo `e garantire un prodotto di qualit`a e di ridurre al minimo i malfunziona-menti dopo il deploy. Gradle a riguardo, offre pieno supporto, con una consistente configurazione di base, per gli ecosistemi JVM, Android, Linux, Windows e ma-cOS mentre per il target JS, necessit`a di configurazioni aggiuntive che verranno analizzate nei paragrafi a seguire.

L’API kotlin.test `e la libreria sviluppata da Kotlin che permette di sviluppare i test per applicazioni mutlipiattaforma, questa racchiude in s´e una serie di annotazioni

(58)

e di funzioni utili a perfomare delle asserzioni indipendentemente dal framework di test usato. I moduli raccolti supportano i test su pi`u piattaforme:

• kotlin-test-common – per asserzioni sul codice comune;

• kotlin-test-annotations-common – annotazioni utili al compilatore durante i test del sorgente comune;

• kotlin-test – un implementazione in JVM di kotlin-test-common;

• kotlin-test-junit e kotlin-test-junit5 forniscono un implementazione della clas-se Asclas-serter in Junit e Junit 5 e delle rispettive annotazioni di kotlin-test-annotations-common;

• kotlin-test-testng - implementazioni di Asserter in TestNG, e mapping delle annotazioni di kotlin-test-annotations-common in annotazioni TestNG; • kotlin-test-js - Implementazioni delle asserzioni e annotazioni comuni di

Ko-tlin in Javascript, con supporto a framework esterno(Jest, Mocha, Karma, Jasmine).

La configurazione base delle dipendenze segue il listato 4.1.

Listato 4.1: Configurazione base nella gestione delle dipendenze per kotlin.test k o t l i n { val c o m m o n M a i n by g e t t i n g { ... } val c o m m o n T e s t by g e t t i n g { d e p e n d e n c i e s { i m p l e m e n t a t i o n ( k o t l i n (" test - c o m m o n ") ) i m p l e m e n t a t i o n ( k o t l i n (" test a n n o t a t i o n s -c o m m o n ") ) } } // JS

(59)

4.1. TESTING 45 js () . c o m p i l a t i o n s [" main "]. d e f a u l t S o u r c e S e t { ... } js () . c o m p i l a t i o n s [" test "]. d e f a u l t S o u r c e S e t { d e p e n d e n c i e s { i m p l e m e n t a t i o n ( k o t l i n (" test - js ") ) } } // JVM jvm () . c o m p i l a t i o n s [" main "]. d e f a u l t S o u r c e S e t { ... } jvm () . c o m p i l a t i o n s [" test "]. d e f a u l t S o u r c e S e t { d e p e n d e n c i e s { i m p l e m e n t a t i o n ( k o t l i n (" test - j u n i t ") ) } } }

Queste dipendenze permettono di usare metodi come assertTrueo assertFalse,

che insieme alle annotazioni @Test, @AfterTest, @BeforeTest e @Ignore consentono di scrivere test in Kotlin.

Listato 4.2: Esempio di test in Kotlin @ T e s t fun t w o S i d e C o n v e r s i o n T e s t () { val d a t e F o r m a t t e d = " 2018 -10 -12 T12 : 0 0 : 0 1 " a s s e r t E q u a l s ( d a t e F o r m a t t e d , d a t e F o r m a t t e d . p a r s e D a t e () . t o D a t e F o r m a t S t r i n g () ) }

Per eseguire test specifici su un target, Kotlin Multipiattaforma mette a dispo-sizione dello sviluppatore dei task predefiniti, che seguono la dicitura ¡nomeTar-get¿Test, inoltre, per eseguirli tutti Kotlin crea il task check.

(60)

Se la configurazione base permette di eseguire test sulla maggior parte delle piat-taforme, il target JS a differenza degli altri dipende da un framework esterno. Con l’aggiornamento alla versione 1.3.40 di Kotlin, la configurazione per questo ambiente `e stata semplificata, aggiungendo il supporto agli ambienti esecutivi No-deJs e Browser (Kotlin 1.3.40 released 2019). I test possono essere eseguiti su pi`u browser a piacimento o su NodeJS se preferibile, Gradle con il DSL mostrato nel listato 4.3, configura Karma1 e le dipendenze NPM automaticamente.

Listato 4.3: DSL configurativo per eseguire test in JS, Karma pu`o essere sosituito con Mocha o NodeJs

k o t l i n { js { // t a r g e t per le a p p l i c a z i o n i non m u l t i p i a t t a f o r m a b r o w s e r { t e s t T a s k { u s e K a r m a { u s e I e () u s e S a f a r i () u s e F i r e f o x () u s e C h r o m e () u s e C h r o m e C a n a r y () u s e C h r o m e H e a d l e s s () u s e P h a n t o m J S () u s e O p e r a () } } } } }

(61)

4.2. DEPLOY 47 I risultati generati dopo l’esecuzione dei task predefiniti, sono consultabili nella cartella ”build/reports/tests” sottoforma di pagina HTML, suddivisi per target in diverse cartelle (4.1).

Figura 4.1: Report in HTML dei risultati dei test, generati attraverso il task ”allTests”

4.2

Deploy

La stragrande maggior parte dei progetti software crea qualcosa che mira a essere consumato in qualche modo. Potrebbe essere una libreria utilizzata da altri pro-getti software o un’applicazione per gli utenti finali. La pubblicazione `e il processo attraverso il quale l’oggetto che viene costruito viene messo a disposizione dei con-sumatori. Prima di comprendere il funzionamento di questa fase `e importante per lo sviluppatore o per il lead programmer comprendere al meglio cosa pubblicare di un progetto multipiattaforma e sopratutto dove. A livello tecnico questi step dipendono soprattutto dalla scelta del repository adatto alla pubblicazione della libreria, i pi`u comuni sono Maven e Ivy per librerie Java, e npm registry per librerie Javascript.

(62)

documenta-zione adeguata, e per eseguire questo nei migliori dei modi esiste il plugin ”Dokka”. Un esempio configurativo basilare `e il listato 4.4

Listato 4.4: Configurazione di Dokka di un progetto multipiattaforma tramite Gradle k o t l i n { // K o t l i n M u l t i p l a t f o r m p l u g i n c o n f i g u r a t i o n jvm () js (" c u s t o m N a m e ") } val d o k k a by g e t t i n g ( D o k k a T a s k ::c l a s s) { o u t p u t D i r e c t o r y = " $ b u i l d D i r / d o k k a " o u t p u t F o r m a t = " html " m u l t i p l a t f o r m {

val c u s t o m N a m e by c r e a t i n g { // The same name as in K o t l i n M u l t i p l a t f o r m plugin , so the s o u r c e s are f e t c h e d a u t o m a t i c a l l y i n c l u d e s = l i s t O f (" p a c k a g e s . md ", " e x t r a . md ") s a m p l e s = l i s t O f (" s a m p l e s / b a s i c . kt ", " s a m p l e s / a d v a n c e d . kt ") } r e g i s t e r (" d i f f e r e n t N a m e ") { // D i f f e r e n t name , so s o u r c e r o o t s must be p a s s e d e x p l i c i t l y t a r g e t s = l i s t O f (" JVM ") p l a t f o r m = " jvm " s o u r c e R o o t { path = k o t l i n . s o u r c e S e t s . g e t B y N a m e (" j v m M a i n ") . k o t l i n . s r c D i r s . f i r s t () .

(63)

4.2. DEPLOY 49 t o S t r i n g () } s o u r c e R o o t { path = k o t l i n . s o u r c e S e t s . g e t B y N a m e (" c o m m o n M a i n ") . k o t l i n . s r c D i r s . f i r s t () . t o S t r i n g () } } } }

Ovviamente, essendo presente la piattaforma JS, l’output della documentazione non pu`o essere javadoc. Il plugin in questione apporta anche un task di nome ”dokka” per produrre effettivamente la documentazione, `e buona pratica esten-dere diversi task di tipo dokkaTask con diversi output se necessario. Mentre il plugin ”org.jetbrains.dokka” permette di produrre la documentazione, i plugin ”com.moowork.node” o ”com.liferay.node”, e ”maven-pubblish” o ”ivy-pubblish”, permettono di pubblicare i sorgenti JS e Java.

4.2.1

Maven Repository e Ivy Repository

La pubblicazione su Maven, a livello tecnico `e molto simile a quella su Ivy per questo i listati mostrati faranno riferimento al primo. In primis, va applicato l’apposito plugin, il listato 4.5, oltre a dimostrare la sua applicazione con Kotlin DSL, indica anche il modo di impostare gli identificativi group e la versione del progetto. Il nome della libreria, inoltre, `e impostato automaticamente da Gradle con il nome della cartella del progetto, tuttavia `e buona pratica esplicare il nome anche nel file di configurazione settings.gradle.kts.

Listato 4.5: Maven Pubblish Plugin p l u g i n s {

/* ... */

Figura

Figura 2.1: Statistiche di popolarit` a tra i build tools
Figura 2.2: Interfaccia di Project, con i metodi principali
Figura 2.4: Rappresentazione DAG dei tasks
Figura 2.6: Grafico delle dipendenze della libreria Hibernate
+6

Riferimenti

Documenti correlati

Gralde che ` e uno strumento navigato nella gestione del codice, ha collaborato con JetBrains nella costruzione di un progetto pi` u grande nominato Kotlin Multipiattaforma,

Se il robot mobile non può essere trasportato nella scuola, una soluzione alternativa sarebbe quella di organizzare la lezione in una Master Class e tenerla in un

 All’atto pratico, se stiamo scrivendo un metodo in cui vogliamo invocarne un altro, ma questo metodo annuncia una throws di un’eccezione controllata che non sappiamo gestire:.

Nel caso di forze non conservative come l’attrito o la resistenza dell’aria (forze dissipative) il lavoro è sempre negativo e porta ad un aumento dell’energia interna del

L’energia cinetica del proiettile (che è anche l’energia cinetica del sistema proiettile +blocco) viene completamente dissipata dalla forza d’attrito e ricompare sotto forma

Molte delle conoscenze trattate in questo documento di tesi sono applicate nel progetto che andremo a illustrare, il quale prevede di sviluppare un’applicazione mobile nativa

Un altra libreria molto usata è graph3 che permette l'utilizzo della grafica delle versione di Turbo Pascal 3.xx, e tramite semplici comandi si riesce a disegnare facilmente

This file contains a comparison, unit by unit, between the automatic analysis carried out by BioVoice and the perceptual one manually performed by the user, and