Mengapa tidak ada yang menggunakan make untuk Java?


162

Hampir setiap proyek Java yang pernah saya lihat menggunakan Maven atau Ant. Mereka adalah alat yang bagus dan saya pikir hampir semua proyek dapat menggunakannya. Tapi apa yang pernah terjadi membuat ? Ini digunakan untuk berbagai proyek non-Jawa dan dapat dengan mudah menangani Java. Tentu Anda harus mengunduh make.exe jika Anda menggunakan Windows, tetapi Ant dan Maven juga tidak datang dengan JDK.

Apakah ada beberapa cacat mendasar dengan make saat digunakan dengan Java? Apakah hanya karena Ant dan Maven ditulis di Jawa?


37
Jika Anda memindahkan barang dan melakukan lebih dari sekadar memanggil kompiler (dan bahkan kemudian), hal-hal khusus platform menjadi sangat canggung untuk ditangani make. Dan memiliki makefile yang hanya berfungsi pada satu sistem tidak terlalu bagus untuk bahasa lintas platform.
Joey

Selain itu, Gradle adalah pemain baru di ruang Java, seperti halnya Gant (pada tingkat yang lebih rendah). Maven atau Semut adalah dikotomi palsu.
Michael Easter

@Michael Easter, dikotomi palsu adalah Maven / Ant vs. Make. Dikotomi yang sebenarnya, jika saya membacanya dengan benar, adalah Gradle / Maven / Gant / Ant vs. Make. Tapi agak sulit untuk mengatakan :)
Dan Rosenstark

Jawaban:


192

Masalah mendasar dengan Make dan Java adalah Make bekerja pada premis yang telah Anda tentukan dependensi, dan kemudian aturan untuk menyelesaikan dependensi itu.

Dengan C dasar, yang biasanya "untuk mengkonversi file main.c ke file main.o, jalankan" cc main.c ".

Anda dapat melakukannya di java, tetapi Anda dengan cepat mempelajari sesuatu.

Sebagian besar kompilator javac lambat untuk memulai.

Perbedaan antara:

javac Main.java
javac This.java
javac That.java
javac Other.java

dan

javac Main.java This.java That.java Other.java

adalah siang dan malam.

Perkuat itu dengan ratusan kelas, dan itu menjadi tidak bisa dipertahankan.

Kemudian Anda menggabungkannya dengan fakta bahwa java cenderung diatur sebagai kelompok file dalam direktori, vs C dan lainnya yang cenderung ke arah struktur yang lebih datar. Make tidak memiliki banyak dukungan langsung untuk bekerja dengan hierarki file.

Make juga tidak pandai menentukan file apa yang kedaluwarsa, pada tingkat pengumpulan.

Dengan Ant, itu akan melewati dan meringkas semua file yang kedaluwarsa, dan kemudian mengkompilasinya dalam sekali jalan. Make hanya akan memanggil kompiler java pada setiap file individual. Setelah melakukan TIDAK, ini membutuhkan alat eksternal yang cukup untuk benar-benar menunjukkan bahwa Make tidak cukup untuk tugas itu.

Itu sebabnya alternatif seperti Ant dan Maven bangkit.


6
Jadi untuk menggunakan make dalam proyek Java yang besar, perlu untuk mempertahankan daftar semua file .java yang diubah dan kemudian memanggil javac di akhir? Kedengarannya kurang ideal bagi saya. Itulah jawaban terbaik yang pernah saya lihat sejauh ini.
Pengguna1

32
Aku suka ini. Penting Anda mengatakan bahwa Semut diperlukan untuk mengatasi kenyataan bahwa javac terlalu lambat.
cmcginty

25
Tidak. Javac tidak lambat jika Anda menggunakannya karena dirancang untuk digunakan. Hanya lambat jika Anda menggunakannya untuk mengkompilasi satu file pada suatu waktu.
Stephen C

7
@Casey javac tidak terlalu lambat. JVM terlalu lambat untuk memulai.
Thorbjørn Ravn Andersen

7
GNU Make setidaknya memiliki $?variabel otomatis yang diperluas ke "semua prasyarat yang lebih baru dari target". Ada juga fitur aturan pola dengan beberapa target yang akan menjalankan resep hanya sekali untuk memperbarui semua .classfile. Menggabungkan dengan beberapa pandai menggunakan file / fungsi teks seperti $(wildcard), $(shell), $(eval)dan Anda bisa mengajarkan makefile Anda untuk menemukan membangun target yang tersebar di seluruh tata letak direktori Anda.
Tanz87

33

Program terhormat makemenangani bahasa yang dikompilasi secara terpisah seperti C dan C ++ dengan cukup baik. Anda mengkompilasi modul, ini digunakan #includeuntuk menarik teks file termasuk lainnya, dan menulis file objek tunggal sebagai output. Compiler sangat banyak merupakan sistem satu per satu, dengan langkah penautan terpisah untuk mengikat file objek ke dalam biner yang dapat dieksekusi.

Namun, di Jawa, kompiler harus benar-benar mengkompilasi kelas lain yang Anda impor import. Meskipun mungkin untuk menulis sesuatu yang menghasilkan semua dependensi yang diperlukan dari kode sumber Java, sehingga makeakan membangun kelas dalam urutan yang benar satu per satu, ini masih tidak akan menangani kasus seperti dependensi melingkar.

Kompiler Java juga bisa lebih efisien dengan caching hasil kompilasi dari kelas lain sambil mengkompilasi kelas lebih lanjut yang bergantung pada hasil yang sudah dikompilasi. Evaluasi ketergantungan otomatis semacam ini tidak mungkin dilakukan makesendirian.


7
Ini sepertinya lebih dari jawaban make versus javac daripada jawaban make versus ant / maven. Berdasarkan jawaban Anda, mengapa seseorang tidak bisa menggunakan make + javac (memberikan javac seluruh paket atau "modul" sekaligus, sehingga dependensi melingkar tersembunyi dari make)? Akankah semut atau pakar memberikan manfaat apa pun atas pendekatan itu?
Laurence Gonsalves

1
@Laurence: Anda bisa memberikan javac seluruh paket sekaligus, tetapi kemudian akan mengkompilasi ulang semua yang ada dalam paket itu (karena itulah yang Anda suruh lakukan). Memang benar bahwa kompiler java cukup cepat, tetapi bahkan lebih cepat jika Anda membiarkannya menentukan kelas mana yang minimum yang diperlukan untuk mengkompilasi ulang setelah mengubah sesuatu.
Greg Hewgill

Apakah Anda merujuk mengatakan javac untuk hanya mengkompilasi kelas "utama" Anda, dan kemudian membuatnya secara otomatis membangun hal-hal yang tergantung padanya? Terakhir saya memeriksa (memang, mungkin di 1.4) yang sangat tidak bisa diandalkan. -Xdepend sedikit lebih baik (tapi lebih lambat, dan masih rusak), dan mereka menghapus bendera itu di 1.3.
Laurence Gonsalves

4
Juga, ini masih tidak menjelaskan mengapa saya menggunakan Ant atau Maven daripada hanya javac lurus ...
Laurence Gonsalves

28

Pertanyaannya didasarkan pada asumsi yang salah: sejumlah pengembang non-sepele memang menggunakan make. Lihat Alat Bangun Java: Ant vs. Maven . Adapun mengapa pengembang tidak akan menggunakan make: banyak pengembang yang tidak pernah menggunakan make, atau menggunakannya dan membencinya dengan api yang membakar lebih panas dari seribu matahari. Karena itu, mereka menggunakan alat alternatif.


10
Kami menggunakannya, dan apinya lebih panas dari seribu satu matahari.
reccles

4
@reccles: Apakah itu hanya kebencian terhadap rekayasa bangunan atau membuat sendiri? Bagaimana semut, Maven, atau sesuatu yang lebih baik (yaitu membuat alat yang buruk untuk kelasnya)?
Pengguna1

5
@ User1 makememiliki banyak "fitur" yang mungkin masuk akal ketika ditulis, tetapi sekarang lebih seperti bug, misalnya, Anda harus menggunakan karakter TAB, bukan spasi, di tempat-tempat tertentu. Hal-hal semacam itu mungkin tidak mengganggu orang-orang yang benar-benar berpengalaman make, tetapi itu membuat kita semua menjadi gila.
Hank Gay

4
@HankGuy: biarkan editor Anda khawatir tentang detail itu. Jika editor Anda tidak dapat menangani tab <-> pengaturan ruang dengan benar, dapatkan editor baru dan Anda akan jauh lebih bahagia. Tapi Anda benar mengatakan bahwa banyak fitur yang ketinggalan zaman seperti cara penanganan dependensi dinamis ( make dependsiapa pun?)
D.Shawley

3
@ User1 Ini skala proyek yang kami bangun. Kami memiliki anggota staf penuh waktu yang menjaga file make dan membangun dependensi. Setelah menggunakan pakar saya menemukan itu lebih mudah dikelola. Sekarang mengatakan bahwa pakar juga tidak sempurna. Tidak ada yang lebih menyebalkan daripada mencoba mencari tahu apa pengaturan XML yang diperlukan untuk melakukan build yang sedikit berbeda dari setup yang ditentukan.
reccles

28

Sebenarnya, make dapat menangani kompilasi ulang dalam satu perintah dari semua file java yang sudah ketinggalan zaman. Ubah baris pertama jika Anda tidak ingin mengkompilasi semua file di direktori atau menginginkan perintah tertentu ...

JAVA_FILES:=$(wildcard *.java)
#
# the rest is independent of the directory
#
JAVA_CLASSES:=$(patsubst %.java,%.class,$(JAVA_FILES))

.PHONY: classes
LIST:=

classes: $(JAVA_CLASSES)
        if [ ! -z "$(LIST)" ] ; then \
                javac $(LIST) ; \
        fi

$(JAVA_CLASSES) : %.class : %.java
        $(eval LIST+=$$<)

4
Bagus! Saya mencari sesuatu seperti itu. Tidak perlu menggunakan alat bangun yang berbeda untuk setiap bahasa saat klasik universal berfungsi dengan baik. Terima kasih!
rsp

17

Semua jawaban lain tentang kelebihan teknis masing-masing adalah benar. Antdan Mavenmungkin lebih cocok untuk Jawa daripada membuat, atau seperti yang ditunjukkan Hank Gay, mereka mungkin tidak :)

Namun, Anda bertanya apakah yang penting Ant dan Maven ditulis di Jawa. Meskipun di StackOverflow kami tidak mempertimbangkan pemikiran seperti itu (tertutup! Tidak terkait pemrograman! Dll.), TENTU SAJA ITULAH BAGIAN DARI HAL. Di rel kami menggunakan Rake, C dudes menggunakan make, dan di Jawa kami menggunakan Ant dan Maven. Meskipun benar bahwa pengembang Semut atau Maven akan menjaga pengembang Java mungkin lebih baik daripada yang lain, ada juga pertanyaan lain: apa yang Anda tulis tugas Ant? Jawa. Jika Anda seorang pengembang Java, itu sangat mudah.

Jadi ya, bagian dari itu adalah menggunakan alat yang ditulis dalam bahasa yang Anda alat.


7
Semut juga muncul pada saat komunitas Jawa tergila-gila dengan XML. (Yang tidak mengatakan XML tidak punya tempat.)
Laurence Gonsalves

3
@Laurence Gonsalves, yang begitu benar. Tapi tolong, kita tidak berbicara tentang mode di sini pada SO :) Saya mengajar Java dev pada saat itu, dan SEMUA adalah XML. Sekarang ini masih XML, tapi tidak ada yang peduli.
Dan Rosenstark

1
Komentar tooling adalah yang menarik. makeberasal dari latar belakang UNIX sehingga perkakas dilakukan dengan menulis utilitas ringkas yang berguna dan menyatukannya. Inilah sebabnya mengapa sebagian besar jahitan dilakukan menggunakan perintah shell.
D.Shawley

2
@D. Shawley, semua benar tentang makedan utilitas Unix kecil. GIT adalah anak dari proses itu juga. Pada catatan pribadi, saya tidak akan mengatakan itu tidak lebih baik. Tapi ini adalah perubahan paradigma besar bagi programmer Java. Semut jauh lebih konsisten dengan cara berpikir Jawa.
Dan Rosenstark

12

Semut dan kemudian Maven dirancang untuk mengatasi beberapa sakit kepala yang disebabkan oleh Make(sambil membuat yang baru dalam proses) Ini hanya evolusi.

... Segera setelah itu, beberapa proyek Java open source menyadari bahwa Ant dapat menyelesaikan masalah yang mereka miliki dengan Makefiles ....

Dari http://ant.apache.org/faq.html#history

Apakah mereka memecahkan sesuatu atau hanya membuat format tambahan untuk dipelajari adalah topik yang subyektif. Yang benar adalah bahwa itu cukup banyak sejarah setiap penemuan baru: Pencipta mengatakan itu memecahkan banyak masalah dan pengguna asli mengatakan itu adalah kebajikan.

Keuntungan utama yang dimilikinya, adalah kemungkinan untuk berintegrasi dengan java.

Saya kira sejarah yang sama akan dengan rakemisalnya.


4
Itu tidak terlalu spesifik. Sakit kepala apa yang disebabkan oleh make yang diselesaikan Maven?
Laurence Gonsalves

1
@Gonsalves: Ya, itu salah satu aspek subyektif dari setiap teknologi baru, pencipta alternatif mengatakan itu memecahkan banyak masalah dan pencipta teknologi yang diganti mengatakan bahwa itu bukan cacat tetapi kebajikan dan sebagainya. Saya pikir dalam situasi khusus ini adalah integrasi java dan kompilasi silang di luar kotak
OscarRyz

2
[Mengacu pada edit jawaban Anda, bukan komentar Anda yang terbaru] Itu masih tidak menjelaskan masalah apa yang dipecahkan, hanya saja semut pencipta mengklaim bahwa masalah telah dipecahkan ...: - / Kesan saya adalah semut awalnya dibuat untuk menjadi alternatif yang lebih sederhana untuk Membuat. Seiring waktu, orang-orang menemukan bahwa ada hal-hal yang hilang sehingga mereka menambahkan fitur sampai Ant menjadi serumit make, tetapi dengan binutils bawaan (membuat sangat bergantung pada alat eksternal seperti cp, rm, dll.), Dan tentu saja ada sintaks XML.
Laurence Gonsalves

2
Ya, tetapi ketika yang terbaik yang dapat dilakukan oleh pencipta suatu alternatif baru adalah mengatakan "ini memecahkan masalah yang sudah ada" tanpa benar-benar mengatakan masalah apa yang tidak begitu berguna bagi mereka yang mempertimbangkan pilihan mana yang akan digunakan. Apakah Ant memecahkan masalah yang saya miliki dengan membuat, atau apakah itu memecahkan hal-hal yang saya tidak anggap sebagai masalah saat memperkenalkan masalah baru bagi saya?
Laurence Gonsalves

9

Salah satu masalah utama yang dipecahkan oleh Maven (dan setup Ant yang diaktifkan Ivy) adalah resolusi ketergantungan otomatis dan pengunduhan guci ketergantungan Anda.


6

Saya pikir penjelasan yang paling mungkin adalah bahwa beberapa faktor tidak mendukung penggunaan make dalam komunitas Jawa dalam periode waktu yang kritis (akhir 1990-an):

  1. Karena Java mencakup banyak platform, programmer Java secara umum tidak begitu mahir pada alat Unix seperti halnya programmer umumnya terbatas pada lingkungan Unix (misalnya, programmer C dan Perl). Perhatikan bahwa ini DI UMUM. Tanpa ragu ada dan programmer Java yang berbakat dengan pemahaman yang mendalam tentang Unix.
  2. Akibatnya mereka kurang mahir membuat dan tidak tahu bagaimana menggunakan make secara efektif.
  3. Meskipun dimungkinkan untuk menulis Makefile pendek dan sederhana yang mengkompilasi Java secara efisien, kehati-hatian diperlukan untuk melakukannya dengan cara yang bebas platform.
  4. Akibatnya ada selera untuk alat membangun platform-intrinsik independen.
  5. Di lingkungan inilah Ant dan kemudian Maven diciptakan.

Singkatnya, sementara make paling pasti dapat digunakan untuk proyek-proyek Java, ada kesempatan untuk menjadikannya alat pembuat Java de facto. Saat itu telah berlalu.


5

Membuat skrip cenderung bergantung pada platform secara inheren. Java seharusnya menjadi platform independen. Oleh karena itu memiliki sistem pembangunan yang hanya berfungsi pada satu platform untuk basis-sumber multi-platform merupakan masalah.


5

Jawaban singkat: Karena maketidak baik. Bahkan di depan C Anda melihat banyak alternatif bermunculan.

Jawaban panjang: makememiliki beberapa kelemahan yang membuatnya hampir tidak cocok untuk mengkompilasi C, dan sama sekali tidak cocok untuk mengkompilasi Java. Anda dapat memaksanya untuk mengkompilasi Java, jika Anda mau, tetapi mengharapkan mengalami masalah, beberapa di antaranya tidak memiliki solusi atau solusi yang cocok. Berikut ini beberapa di antaranya:

Resolusi ketergantungan

makeinheren mengharapkan file memiliki ketergantungan seperti pohon pada satu sama lain, di mana satu file adalah output dari membangun beberapa file lainnya. Ini sudah menjadi bumerang di C saat berurusan dengan file header. makemembutuhkan makefile -specific spesifik yang akan dihasilkan untuk mewakili ketergantungan file C pada file header-nya, sehingga perubahan pada yang terakhir akan menyebabkan sebelum dibangun kembali. Namun, karena file C itu sendiri tidak diciptakan kembali (hanya dibuat kembali), make sering kali mengharuskan menetapkan target sebagai .PHONY. Untungnya, GCC mendukung pembuatan file-file itu secara otomatis.

Di Jawa, dependensi bisa berbentuk lingkaran, dan tidak ada alat untuk dependensi kelas yang menghasilkan otomatis dalam makeformat. ant's Dependtugas dapat, sebaliknya, membaca file kelas langsung, menentukan kelas itu impor, dan menghapus file kelas jika salah satu dari mereka adalah dari tanggal. Tanpa ini, ketergantungan non-sepele dapat menyebabkan Anda dipaksa untuk menggunakan build bersih berulang, menghilangkan keuntungan menggunakan tool build.

Spasi dalam nama file

Meskipun Java atau C tidak menganjurkan menggunakan spasi dalam nama file kode sumber Anda, dalam makehal ini dapat menjadi masalah bahkan jika spasi ada di jalur file. Pertimbangkan, misalnya, jika kode sumber Anda ada di C:\My Documents\My Code\program\src. Ini sudah cukup untuk dipatahkan make. Ini karena makememperlakukan nama file sebagai string. antmemperlakukan jalur sebagai objek khusus.

Memindai file untuk dibangun

makemembutuhkan pengaturan secara eksplisit file mana yang akan dibangun untuk setiap target. antmemungkinkan menentukan folder yang akan dipindai secara otomatis untuk file sumber. Ini mungkin tampak seperti kenyamanan kecil, tetapi pertimbangkan bahwa di Jawa setiap kelas baru membutuhkan file baru. Menambahkan file ke proyek dapat menjadi kerumitan besar dengan cepat.

Dan masalah terbesar dengan make:

make adalah POSIX-dependent

Moto Java adalah "kompilasi sekali dijalankan di mana-mana". Tetapi membatasi kompilasi itu ke sistem berbasis POSIX, di mana dukungan Java sebenarnya yang terburuk, bukanlah niatnya.

Membangun aturan pada makedasarnya adalah bashskrip kecil . Meskipun ada port makeuntuk Windows, agar dapat berfungsi dengan baik, ia harus dibundel dengan port bash, yang mencakup lapisan emulasi POSIX untuk sistem file.

Ini datang dalam dua varietas:

  1. MSYS yang mencoba membatasi terjemahan POSIX ke jalur file, dan karenanya dapat memiliki gotcha yang tidak menyenangkan ketika menjalankan alat eksternal yang tidak dibuat khusus untuk itu.

  2. cygwinyang menyediakan emulasi POSIX lengkap. Program yang dihasilkan, bagaimanapun, cenderung masih mengandalkan lapisan emulasi itu.

Untuk alasan itu, pada Windows, alat bantu pembuatan standar bahkan tidak makesama sekali, melainkan MSBuild, yang juga merupakan alat berbasis XML, pada prinsipnya lebih dekat ant.

Sebaliknya, antdibangun di Jawa, dapat berjalan di mana-mana, dan berisi alat internal, yang disebut "tugas", untuk memanipulasi file dan mengeksekusi perintah dengan cara platform-independen. Cukup fleksibel bahwa Anda sebenarnya bisa lebih mudah membangun program C di Windows antdaripada menggunakan make.

Dan satu minor terakhir:

Bahkan program C tidak menggunakan make native

Anda mungkin awalnya tidak memperhatikan ini, tetapi program C umumnya tidak dikirimkan bersama a Makefile. Mereka dikirim dengan CMakeLists.txt,, atau bashskrip konfigurasi, yang menghasilkan aktual Makefile. Sebaliknya, sumber program Java yang dibangun menggunakan antdikirimkan dengan antskrip yang dibuat sebelumnya. A Makefileadalah produk dari alat lain - Itulah berapa banyak makeyang tidak cocok untuk menjadi alat bangun sendiri. antadalah mandiri, dan menangani semua yang Anda butuhkan untuk proses pembangunan Java Anda, tanpa persyaratan atau dependensi tambahan.

Ketika Anda berjalan antdi platform apa pun, itu Hanya Bekerja (tm). Anda tidak bisa mendapatkannya make. Ini sangat tergantung platform dan konfigurasi.


4

Kecuali saya bukan siapa-siapa anggapan tidak ada (salah) menggunakan make for java salah.

"Mengelola Proyek dengan GNU Make" (tersedia di bawah GFDL) berisi bab lengkap yang didedikasikan untuk digunakan makedengan proyek java.

Karena ini berisi daftar panjang dan buruk dari pro dan kontra menggunakan make bukan alat lain yang mungkin ingin Anda lihat di sana. (lihat: http://oreilly.com/catalog/make3/book/ )


Apakah ini ringkasan yang akurat? make (dalam berbagai rasa) dapat digunakan untuk Jawa, tetapi dengan sedikit rasa sakit. Ant dan Maven (dan jmake ?) Melakukan beberapa hal khusus yang dibutuhkan / disukai Java, untuk membuat proyek Java lebih cepat dan lebih mudah dibangun. Mereka juga dapat digunakan untuk proses pembangunan platform-lebih independen untuk proyek-proyek non-Jawa, tetapi lebih disesuaikan untuk Jawa.
Phil Perry

3

Ant adalah peningkatan berorientasi konfigurasi XML atas Makefiles dan Maven adalah peningkatan ketergantungan alat membangun atas Ant. Beberapa proyek menggunakan ketiganya. Saya pikir proyek JDK digunakan untuk menggunakan campuran makefile dan semut.


1

Salah satu alasan utama adalah bahwa Ant dan Maven (dan kebanyakan alat SCM, CI dan IDE yang ditargetkan java) ditulis dalam java oleh / untuk pengembang java. Ini membuatnya lebih mudah untuk diintegrasikan ke dalam lingkungan pengembangan Anda dan memungkinkan alat-alat lain seperti server IDE dan CI untuk mengintegrasikan bagian dari perpustakaan semut / pakar dalam infrastruktur build / deployment.


1

Suatu hari saya bekerja pada proyek Java yang menggunakan gmake. Ingatan saya kabur tetapi IIRC kami mengalami kesulitan berurusan dengan struktur direktori paket yang diharapkan javac. Saya juga ingat bahwa membangun file JAR itu merepotkan kecuali Anda memiliki sesuatu yang sepele.


1

ApacheAnt bukan sesuatu seperti Make. Make adalah tentang menjelaskan ketergantungan antara file, dan cara membuat file. Semut adalah tentang ketergantungan antara "tugas", dan benar-benar lebih dari cara menempelkan skrip bersama.

mungkin membantu Anda AntVsMake


Saya benar-benar ingin tahu tentang alasan downvotes.
hagello

0

Semut dan Maven mendekati grafik ketergantungan pembangunan dan pengelolaannya dari sudut pandang yang lebih 'modern' ... Tapi seperti yang dikatakan Oscar, mereka menciptakan masalah mereka sendiri sambil berusaha mengatasi masalah lama dengan merek.


0

Saya tidak pernah menggunakan GNU Make untuk proyek Java, tetapi saya dulu menggunakan jmk . Sayangnya itu belum diperbarui sejak tahun 2002.

Itu memiliki beberapa fungsi khusus Java tetapi cukup kecil untuk dimasukkan dalam tarball sumber Anda tanpa secara signifikan meningkatkan ukurannya.

Saat ini saya hanya menganggap setiap pengembang Java yang saya bagikan kode dengan Ant diinstal.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.