Hampir seluruh proses pemrograman sistem komputer dibuat menggunakan compiled language . Seperti namanya, compiled language adalah bahasa pemrograman yang mewajibkan kita untuk melakukan proses compilation agar dapat menggunakan program tersebut.
Intinya adalah, program yang ditulis dengan menggunakan compiled language wajib melalui proses compilation ke dalam bahasa mesin atau bahasa lain yang disebut sebagai intermediate languages .
Java dan C# adalahn contoh dari bahasa yang tidak di- compile ke dalam bahasa mesin, melainkan source code java akan dicompile ke dalam intermediate language dengan nama bytecode . Nantinya bytecode tersebut akan dieksekusi dengan menggunakan tools dengan nama java (ya gua tau nama toolsnya sama dengan nama bahasanya). Hal ini dengan sengaja di desain dengan tujuan compile once, runs everywhere . Sehingga para developer java tidak perlu lagi pusing mikirin buat nulis code spesifik ke satu atau beberapa platform.
Dalam kasus ini, dimana kita menggunakan bahasa pemrograman C, proses compilation akan dilakukan menggunakan compiler yang bernama cc (c compiler) atau gcc (GNU C compiler). Bahasa pemrograman lain seperti java juga memiliki compilernya sendiri, yaitu javac (java compiler). Kalau bahasa assembly memiliki as sebagai compilernya.
Untuk melakukan proses compilation , kita perlu yang namanya source file . Dalam bahasa pemrograman C, source file adalah file yang memiliki ekstensi .c yang berisi instruksi yang akan dijalankan oleh CPU nantinya.
#include <stdio.h>
void echoMsg(char *msg);
int main() {
echoMsg("This file is compiled with c compiler.");
}
void echoMsg(char *msg) {
printf("%s\n", msg);
} Sebagai contoh file main.c di atas, merupakan file yang berisi instruksi untuk mencetak “This file is compiled with c compiler.” ke layar terminal.
Perintah paling simpel untuk melakukan proses compilation adalah kita hanya perlu menjalankan perintah cc main.c atau gcc main.c di terminal.
gcc main.c # atau cc main.c Perintah di atas akan menerjemahkan source file kita ke bahasa mesin dan menghasilkan file baru dengan nama a.out pada platform Linux atau a.exe pada platform Windows yang merupakan file hasil compilation dari file main.c . Dan untuk mengeksekusi program tersebut, kita perlu menjalankan perintah ./a.out atau a.exe di terminal.
Simpel kan ? hanya dengan 1 perintah, kita sudah dapat menjalankan program yang telah kita buat. Namun sebenarnya, perintah cc main.c tersebut menjalankan 2 proses, yaitu compiling dan linking . Kedua fase inilah dilalui agar source code kita dapat dijalankan oleh CPU.
2 phases for building software
Seperti yang sudah dijelaskan sebelumnya, terdapat 2 fase yang dibutuhkan untuk membuat agar program kita dapat di-eksekusi. 2 fase ini berjalan secara berurutan, yakni:
- Compiling
- Linking
Proses compiling akan mengubah source code kita menjadi apa yang disebut dengan object code dengan ekstensi .o . Proses penerjemahan source code kita menjadi bentuk object code ini tidak hanya terjadi pada bahasa C saja, melainkan mayoritas bahasa pemrograman lain juga, seperti C++, Java, Assembly, Pascal, dll
Lalu kenapa kita hanya perlu menggunakan 1 perintah saja, yakni cc main.c ? Dalam kasus kita, perintah tersebut akan menjalankan proses compiling dan linking secara otomatis.
Kecuali kita menambahkan option flag -c pada perintah cc , seperti cc -c <nama file> , maka proses linking tidak akan dijalankan secara otomatis.
Dan jika kita mencoba menjalankan perintah cc -c main.c , maka hasilnya akan menghasilkan file baru dengan nama main.o yang merupakan file object code dari file main.c . Dan file object code inilah yang nantinya akan kita gabungkan dengan object file lainnya atau library code lainnya untuk menghasilkan executable file yang bisa dijalankan dan dibaca oleh komputer.
gcc -c main.c # atau cc -c main.c Setelah berhasil menghasilkan object file dari source code kita, maka perintah selanjutnya yang bisa kita gunakan untuk menghasilkan executable file dari object file adalah dengan menggunakan command ld -o <output file> <input file> atau gcc -o <output file> <input file> .
gcc -o main main.o # atau ld -o main main.o Command diatas akan menghasilkan executable file baru dengan nama main.exe (windows) atau main.out (linux) yang merupakan executable file dari source file main.c . Hasil output tersebut merupakan hasil output yang sama dengan menjalankan perintah gcc -o main main.c .
Dan untuk mengeksekusi executable file yang telah kita buat, kita hanya perlu menjalankan perintah ./main atau main.exe di terminal.
./main # atau main.exe Program compilation process
Seperti yang sudah dijelaskan sebelumnya, sebagai seorang developer, kita memiliki pilihan untuk membangun software kita dengan 1 step atau 2 step.
Secara umum, ketika kita menjalankan perintah untuk melakukan proses compilation , maka compiler akan melakukan 2 proses secara default dan berurutan, yaitu compiling dan linking . Kedua proses tersebut yang nantinya akan menghasilkan executable file yang dapat dibaca dan dijalankan oleh komputer.
Dan ketika kita menjalankan file executable tersebut, maka CPU akan melakukan proses execution untuk mengeksekusi instruksi-instruksi yang ada di dalam file tersebut.
Rangkuman dari seluruh proses tersebut dapat dilihat pada diagram simpel di bawah ini:
Ketika sudah sampai ke titik ini, salah satu diantara kalian pasti ada yang bertanya-tanya, “Ribet banget mau pake 2 proses segala, bukannya bisa pake 1 langkah saja ?” .
Jawabannya adalah, “lu jangan ngaku programmer kalo lu gak mau pake cara yang lebih ribet. Sudah kodrat kita buat menggunakan cara yang paling ribet biar keliatan keren coy” .
Sorry bercanda. Jawaban sebenarnya adalah untuk 4 atau lebih alasan ini:
- modularity
- reusability
- recompilation speed
- debugging
- dll (thx chatgpt)
Keyword-keyword diatas adalah keyword favorit para developer. Apalagi pas presentasi di depan orang umum. Tapi dalam kasus ini, keyword tersebutlah yang menjadi alasan kenapa kita mau memisahkan proses membuat executable file kita menjadi 2 proses.
Sekarang bayangin deh, kita ada 4 file yang masing-masing kita lakukan step 1, yaitu compilation , sehingga menjadi:
main.c => main.o
input.c => input.o
process.c => process.o
output.c => output.o Dan untuk menjadikan keempat file tersebit menjadi 1 executable file , maka kita perlu melakukan step ke 2, yaitu linking dengan command:
gcc -o mainprogram main.o input.o process.o output.o Nah sekarang, seandainya file process.o berubah isinya, maka kita hanya perlu melakukan compilation pada file process.c saja tanpa perlu melakukan recompilation pada semua file setiap ada perubahan kecil di project kita.
Yang kedua, ketika kita melakukan compile pada setiap file dan menemukan error pada proses compilation pada salah satu file, maka kita hanya perlu meng-fix error pada file tersebut saja tanpa perlu meng-fix error pada file lainnya.
Yang ketiga, object file yang kita generate itu bisa digunakan di banyak project lainnya. Anggepannya, kita bikin shared general-purpose library sendiri. Kita akan melihat ini di materi kedepan.
Dan buat yang kepikiran, kenapa gak include setiap file yang kita gunain ke dalem main.c , kayak begini:
#include "input.c"
#include "process.c"
#include "output.c"
int main() {
...
} Lalu lu semua tinggal compile main.c saja, dan hasilnya akan langsung jadi executable file yang bisa dijalankan, kayak begini:
gcc -o mainprogram main.c # tada! pendek kan ? Lebih baik buang jauh-jauh pikiran busuk kalian, karena cara tersebut agak kurang direkomendasikan.
Karena cara tersebut sama aja kayak kalian men-copy code di file lain ke dalem file main.c punya kalian. Dan proses compilation akan lebih lama karena compiler harus membaca keseluruhan code yang ada di file main.c beserta file lainnya.
Kecuali kalian cuma pengen testing-testing aja, bikin aplikasi kecil-kecilan, cara tersebut sangatlah tidak direkomendasikan.