Setiap kali kita mengetik g++ utama.cpp matematika.cpp -o utama, compiler seolah mengerjakan semua file sekaligus dari awal sampai akhir. Kenyataannya tidak: compiler bekerja satu file pada satu waktu, buta terhadap file lain, dan hasilnya bukan langsung program melainkan potongan-potongan object file yang baru kemudian disambung oleh linker. Model inilah — separate compilation — yang membuat proyek dengan ribuan file mungkin, dan yang menjelaskan kenapa build system seperti make sampai diciptakan. Pada Chapter ini kita jalankan kedua tahapnya secara manual dengan flag -c, periksa object file yang dihasilkan, dan ukur sendiri manfaat kompilasi inkremental.
Translation Unit: Unit Kerja Compiler #
Istilah kuncinya adalah translation unit (TU): satu file .cpp setelah preprocessing — semua #include sudah disalin masuk, semua makro sudah diganti. Compiler menerima satu TU, memeriksanya sampai tuntas, dan mengeluarkan satu object file .o berisi machine code yang belum final. Dua hal penting menyusul dari definisi ini. Pertama, compiler sama sekali tidak tahu isi TU lain: kalau utama.cpp memanggil total(), compiler hanya percaya pada deklarasinya di header dan menandai panggilan itu “akan diselesaikan nanti”. Kedua, pemeriksaan kesesuaian deklarasi-versus-definisi lintas file sepenuhnya jadi tugas linker pada tahap kedua — sebab itulah error linking punya wajah berbeda dari error kompilasi.
Flag -c (compile only) memerintahkan g++ berhenti setelah menghasilkan object file, tanpa linking. Kita uji pada modul statistik — header, source, dan pemakainya:
#include <vector>
int total(const std::vector<int>& data);
double rataRata(const std::vector<int>& data);#include "statistik.h"
int total(const std::vector<int>& data) {
int jumlah = 0;
for (int n : data) {
jumlah += n;
}
return jumlah;
}
double rataRata(const std::vector<int>& data) {
if (data.empty()) {
return 0.0;
}
return static_cast<double>(total(data)) / data.size();
}#include <iostream>
#include <vector>
#include "statistik.h"
int main() {
std::vector<int> penjualan {120, 85, 200, 95};
std::cout << "Total : " << total(penjualan) << "\n";
std::cout << "Rata-rata : " << rataRata(penjualan) << "\n";
return 0;
}Dua Tahap: Kompilasi per File, Lalu Link #
Kompilasi kedua TU satu per satu. Perintah yang sukses mencetak apa pun tidak — diam adalah tanda berhasil:
g++ -std=c++23 -Wall -Wextra -c utama.cpp
g++ -std=c++23 -Wall -Wextra -c statistik.cpp
ls -l *.o | awk '{print $5, $9}'Output yang Dihasilkan:
6424 statistik.o
32384 utama.oDua object file kini ada di disk — utama.o lebih gemuk karena membawa semua mesin iostream yang dipakai main(). Periksa jati dirinya dengan file:
file utama.o | cut -d, -f1-2
g++ utama.o statistik.o -o laporan
./laporanOutput yang Dihasilkan:
utama.o: ELF 64-bit LSB relocatable, x86-64
Total : 500
Rata-rata : 125Kata relocatable pada output file adalah tanda object file: machine code sudah ada, tetapi alamat dan simbol antar-file belum dikunci. Menyambungkannya kini bisa kita lakukan terpisah dari kompilasi — g++ pada file .o langsung menjalankan linker tanpa menyentuh source code. Diagram keseluruhannya:
Pola ekstensinya yang menjadi kunci: satu .cpp menghasilkan tepat satu .o, dan linker menerima semua .o sekaligus. Kita tidak pernah meng-include file .cpp — melainkan membiarkan linker yang menyatukannya.
Melacak Dependensi Secara Manual #
Object file yang tersimpan di disk membuka kemampuan yang tadinya tak terlihat: kompilasi ulang yang selektif. Ubah statistik.cpp (di sini cukup touch untuk mensimulasikan perubahan), lalu bandingkan pekerjaan yang dibutuhkan:
touch statistik.cpp
g++ -std=c++23 -Wall -Wextra -c statistik.cpp
g++ utama.o statistik.o -o laporan
./laporanOutput yang Dihasilkan:
Total : 500
Rata-rata : 125Hanya satu file yang dikompilasi ulang — utama.o lama masih dipakai apa adanya, dan linker tinggal menyambung ulang. Aturan manualnya:
- File
.cppberubah → kompilasi ulang.omilik file itu saja, lalu link ulang. - Header berubah → kompilasi ulang semua
.cppyang meng-include-nya, langsung maupun lewat header lain.
Kenapa Incremental Build Penting #
Untuk tiga file, melacak semuanya dengan tangan masih bisa diikuti. Untuk proyek sungguhan — ratusan .cpp yang saling meng-include — pelacakan manual bukan hanya melelahkan, tetapi juga tidak bisa diandalkan. Hitungannya sederhana: tanpa object file, mengubah satu baris di satu file berarti mengompilasi ulang seluruh proyek; dengan object file, yang perlu diulang hanya file yang benar-benar terdampak, plus satu kali linking. Pada proyek besar, perbedaan itu antara menunggu beberapa detik versus berjam-jam setiap iterasi — dan build system seluruh dunia (make, Ninja, CMake, hingga sistem build IDE Anda) pada intinya hanyalah otomatisator aturan dua baris di atas: kapan file .o harus dibuat ulang, dan kapan cukup link ulang.
Kita sudah membuktikan setiap potongan modelnya secara manual: -c per file, object file yang bisa tersimpan, dan link yang berdiri sendiri. Persoalan tersisa murni soal pengelolaan — dan untuk itu ada tool yang tugasnya persis menjalankan aturan-aturan tadi tanpa lupa: make. Chapter berikutnya membangun Makefile pertama kita untuk proyek statistik ini.
Referensi: Link Options di dokumentasi GCC .