Implementați JAR-uri grase. Reporniți procesele.

sbtsbt-assembly

sbt-assembly este un plugin sbt portat inițial din assembly-sbt de codahale, care cred că a fost inspirat de pluginul de asamblare al lui Maven. Scopul este simplu: creați un JAR gras al proiectului dvs. cu toate dependențele sale.

  • sbt
  • Dorința arzătoare de a avea o procedură simplă de implementare.

Probleme de raportare și contribuții

Înainte de a-mi trimite un e-mail, vă rugăm să citiți cu atenție Ghidul de raportare a problemelor. De două ori. (Nu-mi trimiteți un e-mail)

Utilizarea pluginului publicat

Adăugați sbt-assembly ca dependență în project/plugins.sbt:

(Este posibil să fie necesar să verificați etichetele acestui proiect pentru a vedea care este cea mai recentă versiune.)

Deoarece sbt-assembly este acum un plugin automat care este declanșat pentru toate proiectele cu JvmPlugin, nu ar trebui să necesite o configurare suplimentară pentru a include sarcina de asamblare în proiectul dvs. Consultați ghidul de migrare pentru detalii despre cum să faceți upgrade de la asamblarea sbt mai veche.

Aplicarea pluginului la multi-proiect build.sbt

De exemplu, iată un build.sbt cu mai multe proiecte:

În exemplul de mai sus, atât proiectul aplicației, cât și proiectul utils nu rulează teste în timpul asamblării. Proiectul aplicației stabilește o clasă principală, în timp ce proiectul utils stabilește numele fișierului său jar.

Acum veți avea o nouă sarcină minunată de asamblare care vă va compila proiectul, vă va rula testele și apoi va împacheta fișierele de clasă și toate dependențele într-un singur fișier JAR: target/scala_X.X.X/projectname-assembly-X.X.X.jar .

Dacă specificați un mainClass în asamblare în build.sbt (sau doar lăsați-l să detecteze automat unul), atunci veți ajunge cu un JAR complet executabil, gata de rock.

Iată lista cheilor pe care le puteți reconecta pentru sarcina de asamblare.

De exemplu, numele borcanului poate fi setat după cum urmează în build.sbt:

Pentru a sări peste test în timpul asamblării,

Pentru a seta o clasă principală explicită,

Excluderea unei clase principale explicite din asamblarea dvs. necesită totuși ceva puțin diferit

Dacă mai multe fișiere partajează aceeași cale relativă (de exemplu, o resursă denumită application.conf în JAR-uri cu dependență multiplă), strategia implicită este de a verifica dacă toți candidații au același conținut și se erorează altfel. Acest comportament poate fi configurat pe bază de cale, utilizând una dintre următoarele strategii încorporate sau scriind una personalizată:

  • MergeStrategy.deduplicate este valoarea implicită descrisă mai sus
  • MergeStrategy.first alege primul dintre fișierele potrivite în ordinea clasei
  • MergeStrategy.last alege ultima
  • MergeStrategy.singleOrError se salvează cu un mesaj de eroare la conflict
  • MergeStrategy.concat pur și simplu concatenează toate fișierele potrivite și include rezultatul
  • MergeStrategy.filterDistinctLines, de asemenea, concatenează, dar lasă duplicatele pe parcurs
  • MergeStrategy.rename redenumește fișierele provenite din fișiere jar
  • MergeStrategy.discard aruncă pur și simplu fișierele potrivite

Cartarea numelor de căi pentru strategiile de îmbinare se face prin setarea assemblyMergeStrategy care poate fi mărită după cum urmează:

NOTĂ:

  • assemblyMergeStrategy în asamblare așteaptă o funcție. Nu puteți face assemblyMergeStrategy în assembly: = MergeStrategy.first !
  • Unele fișiere trebuie să fie eliminate sau redenumite altfel pentru a evita ruperea codului zip (din cauza numelui fișierului duplicat) sau a licenței legale. Delegați manipularea implicită la (assemblyMergeStrategy în asamblare) ca exemplu de potrivire a modelului de mai sus.

Apropo, primul model de caz din cele de mai sus folosind PathList (.) Este modul în care puteți alege javax/servlet/* din primul borcan. Dacă MergeStrategy.deduplicate implicit nu funcționează pentru dvs., probabil înseamnă că aveți mai multe versiuni ale unei biblioteci extrase de graficul de dependență. Soluția reală este să remediați acel grafic de dependență. Puteți rezolva problema prin MergeStrategy.first, dar nu vă mirați când vedeți ClassNotFoundException .

Iată valoarea implicită:

Custom MergeStrategy poate afla de unde provine un anumit fișier folosind metoda sourceOfFileForMerge pe sbtassembly.AssemblyUtils, care ia directorul temporar și unul dintre fișierele trecute în strategie ca parametri.

Pluginuri de strategie de îmbinare a terților

Suportul pentru strategiile de îmbinare a cazurilor speciale dincolo de domeniul de aplicare generic poate fi oferit de pluginurile însoțitoare, mai jos este o listă neexhaustivă:

sbt-assembly poate umbra clasele din proiectele dvs. sau din dependențele bibliotecii. Susținută de Jar Jar Links, transformarea bytecode (prin ASM) este utilizată pentru a schimba referințele la clasele redenumite.

Iată regulile de umbră:

  • ShadeRule.rename ("x. **" -> "y. @ 1",.). ÎnToate Aceasta este regula principală.
  • ShadeRule.zap („a.b.c”). InAll
  • ShadeRule.keep ("x. **"). InAll

Regula principală ShadeRule.rename este utilizată pentru a redenumi clasele. Toate referințele la clasele redenumite vor fi, de asemenea, actualizate. Dacă un nume de clasă este asociat cu mai multe reguli, se va aplica doar prima. Regulile de redenumire iau un vararg de perechi de șiruri

este un nume de clasă cu metacaractere opționale. ** se va potrivi cu orice sub șir valid de nume de clasă. Pentru a se potrivi cu o singură componentă de pachet (prin excluderea. Din potrivire), se poate folosi în schimb un singur *. este un nume de clasă care poate face referință opțional la șirurile potrivite de metacaractere. O referință numerotată este disponibilă pentru fiecare * sau ** din

, începând de la stânga la dreapta: @ 1, @ 2 etc. O referință specială @ 0 conține întregul nume al clasei potrivite.

În loc de .inAll, apelați .inProject pentru a se potrivi cu sursa proiectului dvs. sau apelați .inLibrary ("commons-io"% "commons-io"% "2.4",.) Pentru a se potrivi dependențelor specifice bibliotecii. inProject și inLibrary (.) pot fi înlănțuite.

Regula ShadeRule.zap face ca orice clasă potrivită să fie eliminată din fișierul jar rezultat. Toate regulile zap sunt procesate înainte de redenumirea regulilor.

Regula ShadeRule.keep marchează toate clasele potrivite ca „rădăcini”. Dacă sunt definite reguli de păstrare, toate clasele care nu sunt accesibile din rădăcini prin analiza dependenței sunt eliminate atunci când scriem jarul de ieșire. Acesta este ultimul pas al procesului, după redenumire și zapping.

Pentru a vedea ieșirea detaliată pentru umbrire:

Clasele Scala conțin o adnotare care, printre altele, conține toate simbolurile la care se face referire în acea clasă. Începând cu sbt-assembly XXX, regulile de redenumire vor fi aplicate și acestor adnotări, ceea ce face posibilă compilarea sau reflectarea împotriva unei biblioteci umbrite.

Acest lucru este limitat în prezent la redenumirea pachetelor. Redenumirea numelor de clase nu va funcționa și va provoca erori de compilare atunci când compilați cu biblioteca umbrită.

Cu excepția JAR-urilor și fișierelor

Dacă trebuie să spuneți sbt-assembly să ignore JAR-urile, probabil că faceți greșit. sarcina de asamblare apucă JAR-uri din clasa proiectului dvs. Încercați mai întâi să reparați calea de curs.

Dacă încercați să excludeți fișierele JAR care fac deja parte din container (cum ar fi Spark), luați în considerare extinderea bibliotecii dependente la configurația „furnizată”:

Maven definește „furnizat” ca:

Acest lucru seamănă mult cu compilarea, dar indică faptul că vă așteptați ca JDK sau un container să furnizeze dependența în timpul rulării. De exemplu, atunci când creați o aplicație web pentru Java Enterprise Edition, veți seta dependența de API-ul Servlet și API-urile Java EE aferente domeniului de aplicare furnizat, deoarece containerul web oferă acele clase. Acest domeniu de aplicare este disponibil numai pe compilarea și testarea clasei și nu este tranzitiv.

Dependența va face parte din compilare și test, dar exclusă din runtime. Dacă oamenii Spark doresc să includă dependențe „furnizate” înapoi pentru a rula, @douglaz a venit cu o soluție one-liner pe StackOverflow sbt: cum pot adăuga dependențe „furnizate” înapoi pentru a rula/testa clasa activităților?:

Excludeți depozite tranzitive specifice

S-ar putea să vă gândiți să excludeți fișierele JAR din cauza conflictelor de îmbinare. Combinați conflictul de fișiere * .class indică calea patologică a clasei, adesea datorită fișierelor JAR non-modulare sau SLF4J, nu problemei asamblării. Iată ce se întâmplă atunci când încercați să creați un JAR gras cu Spark inclus:

În cazul de mai sus, două fișiere JAR separate javax.servlet-2.5.0.v201103041518.jar și servlet-api-2.5-20081211.jar definesc javax/servlet/SingleThreadModel.class! În mod similar, de asemenea, conflictele asupra comun-beanutils și EsotericSoftware/minlog. Iată cum să evacuați depozite tranzitive specifice:

Uneori este nevoie de un pic de muncă de detectiv pentru a afla ce depozite tranzitive trebuie excluse. Joaca! vine cu sarcina dist, deci nu este necesară asamblarea, dar să presupunem că am vrut să rulăm asamblarea. Aduce semnpost-commonshttp4, ceea ce duce la înregistrarea în comun. Acest lucru intră în conflict cu jcl-over-slf4j, care reimplementează API-ul de înregistrare. Deoarece depozitele sunt adăugate prin build.sbt și playScalaSettings, iată o modalitate de a lucra în jurul acestuia:

Cu excepția fișierelor specifice

Pentru a exclude anumite fișiere, personalizați strategia de îmbinare:

Împărțirea proiectului și a depozitelor JAR-uri

Pentru a crea un fișier JAR care conține doar dependențele externe, tastați

Acest lucru este destinat să fie utilizat cu un JAR care conține doar proiectul dvs.

NOTĂ: Dacă utilizați opțiunea -jar pentru java, aceasta va ignora -cp, deci dacă aveți mai multe fișiere JAR trebuie să utilizați -cp și să treceți clasa principală: java -cp "jar1.jar: jar2.jar" Principal

Cu excepția JAR-urilor din biblioteca Scala

Pentru a exclude biblioteca Scala (JAR-urile care încep cu scala- și sunt incluse în distribuția binară Scala) pentru a rula cu comanda scala,

Dacă toate eforturile eșuează, iată o modalitate de a exclude fișierele JAR:

De asemenea, puteți adăuga amprenta SHA-1 la numele fișierului de asamblare, acest lucru vă poate ajuta să determinați dacă s-a schimbat și, de exemplu, dacă este necesar să implementați dependențele,

În mod implicit, din motive de performanță, rezultatul dezarhivării oricăror fișiere JAR de dependență pe disc este stocat în cache de la rula la rula. Această caracteristică poate fi dezactivată prin setarea:

În plus, JAR-ul de grăsime este stocat în cache, astfel încât marcajul său de timp se modifică numai atunci când intrarea se modifică. Această caracteristică necesită verificarea hasha SHA-1 a tuturor fișierelor * .class și a hashului tuturor fișierelor * .jar de dependență. Dacă există un număr mare de fișiere de clasă, acest lucru ar putea dura mult, deși viteza a fost îmbunătățită mai degrabă cu hashing de fișiere jar, mai degrabă decât conținutul lor. Această caracteristică poate fi dezactivată prin setarea:

Prependerea unui script de lansare

Puteți prepara un script de lansare în vasul de grăsime. Acest script va fi un script shell și batch valid și va face jarul executabil pe Unix și Windows. Dacă activați shebang-ul, fișierul va fi detectat ca executabil sub Linux, dar acest lucru va face să apară un mesaj de eroare pe Windows. Pe Windows, pur și simplu adăugați un „.bat” la numele fișierelor pentru al face executabil.

Acest lucru va pregăti următorul script shell în borcan.

De asemenea, puteți alege să preparați doar script-ul shell la vasul de grăsime după cum urmează:

Publicare (nerecomandat)

Publicarea JAR-urilor grase în lume este descurajată, deoarece JAR-urile nemodulare provoacă multă tristețe. S-ar putea crede că non-modularitatea este comoditate, dar se transformă rapid într-o durere de cap în momentul în care utilizatorii tăi trec în afara codului de exemplu Hello World. Dacă totuși doriți să publicați artefactul asamblat împreună cu sarcina de publicare și toate celelalte artefacte, adăugați un clasificator de asamblare (sau altul):

Î: În ciuda prietenilor preocupați, doresc în continuare să public JAR-uri grase. Ce sfaturi ai?

Probabil că ar trebui să înființați o afacere frontală pentru a vă minți despre dependențele pe care le aveți în pom.xml și ivy.xml. Pentru a face acest lucru, creați un subproiect în scop JAR de grăsime numai acolo unde depindeți de dependențe și creați un al doilea subproiect cosmetic pe care îl utilizați numai în scopuri de publicare:

Publicat sub Licența MIT, consultați LICENȚA

Despre

Implementați JAR-uri grase. Reporniți procesele. (portul codahale/assembly-sbt)