VietTechiesHIỂU CÔNG NGHỆ. MỞ GÓC NHÌN.
☰ Chuyên mục

CMake giúp biên dịch bộ công cụ xử lý ảnh y khoa trên Windows và Unix bằng cách sinh tệp cho các công cụ lập trình quen thuộc

24/10/2026 · VietTechies · Biên tập

Máy trạm Sun Ultra 1 trên bề mặt sáng màu, minh họa máy trạm Unix cuối thập niên 1990.


Năm 1999, một dự án xử lý ảnh y khoa mới gặp bài toán build — tạo phần mềm từ mã nguồn — mà sau này dẫn tới sự ra đời của CMake: các lập trình viên làm việc trên cả máy trạm Unix lẫn máy tính Windows. Bộ công cụ Insight Segmentation and Registration Toolkit (ITK) cần build ổn định trên cả hai nền tảng, nhưng duy trì các tập lệnh build riêng biệt khiến nhóm liên tục gặp vướng mắc.

Khi đó, các dự án C++ đa nền tảng thường dùng Makefiles trên Unix song song với các tệp dự án Microsoft Visual Studio chỉnh sửa bằng tay. Bill Hoffman và Kenneth Martin tại Kitware nhận thấy mỗi khi một kỹ sư thêm tệp mã nguồn, cả hai cấu hình đều phải cập nhật thủ công, dễ khiến chúng không còn khớp nhau và làm quá trình build thất bại.

Thay vì buộc lập trình viên Windows dùng dòng lệnh hoặc yêu cầu lập trình viên Unix duy trì các tệp dành cho môi trường phát triển tích hợp (IDE), Hoffman bắt tay thiết kế CMake vào năm 2000. Ông chọn một cách đơn giản: viết một bản mô tả build, rồi để phần mềm sinh ra các tệp dự án đúng định dạng mà từng nền tảng vốn sử dụng.

Khi các giải pháp thỏa hiệp dùng dòng lệnh thất bại

Trước khi viết CMake, các kỹ sư đã trải qua thất bại của hai kiến trúc mang tính thỏa hiệp. Khi đó, Visualization Toolkit (VTK) giải quyết bài toán build đa nền tảng bằng cách duy trì các tập lệnh configure trên Unix song song với một tiện ích Windows tên là pcmaker. Viết bằng C, pcmaker phân tích Makefiles của Unix để sinh tệp cho công cụ build NMake của Microsoft. Tuy nhiên, nhóm lại đưa thẳng bản thực thi đã biên dịch của tiện ích vào kho mã Concurrent Versions System (CVS) của dự án. Những công việc thông thường, như thêm một thư viện, thường buộc lập trình viên sửa mã nguồn tiện ích rồi đưa một bản thực thi mới vào hệ thống quản lý phiên bản.

Dự án thị giác máy tính TargetJr trên máy trạm Sun thử cách thứ hai: chuyển hệ thống imake sang môi trường gmake có thể gọi trình biên dịch trên cả Unix và Windows. Nhưng hệ thống này phụ thuộc vào một tập hợp biến môi trường của trình thông dịch lệnh (shell) dễ gặp trục trặc. Chỉ cần môi trường của người dùng thiếu một đường dẫn, quá trình build sẽ thất bại với nguyên nhân cực kỳ khó xác định. Vấn đề căn bản hơn là cả hai hệ thống đều buộc những lập trình viên Windows giàu kinh nghiệm dùng dòng lệnh. Hoffman và Martin nhận thấy điều đó khiến lập trình viên Windows có xu hướng tự tạo tệp dự án Visual Studio bằng tay rồi đóng góp các tệp ấy cho dự án, tái tạo lại tình trạng hai hệ thống build song song.

Sinh tệp cho công cụ sẵn có thay vì thay thế công cụ

Hoffman và nhóm Kitware kết luận rằng muốn hoạt động hiệu quả, công cụ build không thể ép lập trình viên thay đổi thói quen. Nếu các kỹ sư Windows muốn dùng tệp không gian làm việc của Visual Studio, hệ thống build phải sinh ra đúng loại tệp đó. Yêu cầu này quyết định những ràng buộc trong kiến trúc CMake.

Thứ nhất, CMake không được phụ thuộc vào thứ gì ngoài trình biên dịch C++ đã cài sẵn. Khi đó, biên dịch và cài đặt các ngôn ngữ kịch bản như Tcl là việc khó trên nhiều nền tảng Unix và Windows. Để không cần thêm thành phần phụ thuộc nào ngoài trình biên dịch C++, nhóm chọn cách tạo một ngôn ngữ cấu hình gọn nhẹ riêng cho CMake. Thứ hai, CMake phải chạy được các chương trình sinh mã trong quá trình build. Nhờ đó, hệ thống hỗ trợ những quy trình như VTK phân tích tệp tiêu đề C++ để sinh mã kết nối với Python, Tcl và Java ngay trong quá trình biên dịch. Thứ ba, CMake phải lưu các tệp sinh ra khi build trong một cây thư mục riêng, tách khỏi mã nguồn, tránh để các tệp mã đối tượng làm lộn xộn kho quản lý phiên bản.

Để đáp ứng những yêu cầu đó, CMake chia quá trình build thành hai giai đoạn riêng: cấu hình (configure) và sinh tệp (generate). Ở bước cấu hình, CMake đọc CMakeCache.txt nếu tệp này còn từ lần chạy trước, rồi xử lý tệp CMakeLists.txt trong thư mục gốc của dự án. CMake thực thi các lệnh để dựng trong bộ nhớ một đồ thị nội bộ gồm các mục tiêu build: thư viện, tệp thực thi và lệnh tùy chỉnh. Chỉ khi đồ thị đã hoàn chỉnh, bước sinh tệp mới bắt đầu. Ở giai đoạn thứ hai này, CMake chuyển dữ liệu biểu diễn các mục tiêu cho một bộ sinh tệp (generator) dành riêng cho từng nền tảng. Bộ sinh chuyển trực tiếp các mục tiêu thành tệp đúng định dạng của công cụ sẵn có: Makefiles cho Unix hoặc các tệp dự án trong không gian làm việc của Visual Studio.

Cái giá khi làm việc trong IDE

Sinh tệp build cho công cụ sẵn có giúp lập trình viên chấp nhận sử dụng hệ thống, nhưng cũng đặt ra một giới hạn kỹ thuật chặt chẽ. Một công cụ build tự thiết kế, tự thực thi mọi bước biên dịch, có thể tùy ý áp dụng các kỹ thuật lập lịch và quy tắc phụ thuộc. Ngược lại, bộ sinh tệp không thể bảo đảm một cách thức build mà bản thân công cụ bên dưới không thực hiện được.

Như Hoffman và Martin sau này nhìn lại trong bài phân tích kiến trúc, CMake phải giới hạn khả năng của mình ở những gì các IDE và hệ thống build sẵn có đều hỗ trợ. Hỗ trợ tệp build dành cho các IDE ấy khiến chính CMake khó phát triển hơn. Dù vậy, Kitware chấp nhận cái giá đó vì giữ cho lập trình viên được dùng công cụ quen thuộc quan trọng hơn một thiết kế đẹp về lý thuyết. Khi cho phép kỹ sư nhấn phím build thông thường trong Visual Studio hoặc gõ make trong cửa sổ dòng lệnh Unix, CMake giao phần xử lý sự phức tạp của từng nền tảng cho bộ sinh tệp, mà không buộc lập trình viên bỏ thói quen làm việc hằng ngày.

Ưu tiên quy trình chạy được hơn mô hình trừu tượng thuần túy

Cách làm thực dụng này sớm mang lại kết quả. CMake build thành công ITK ngay từ những mốc phát triển đầu tiên. Năm 2002, nhóm chuyển hệ thống build của VTK và ParaView sang CMake, thu hút nguồn tài trợ hợp tác từ các phòng thí nghiệm quốc gia Hoa Kỳ, trong đó có Los Alamos, Sandia và Lawrence Livermore.

Với các nhóm xây dựng hạ tầng dùng chung, quá trình phát triển của CMake gợi ra một câu hỏi thực tế: những người đóng góp đang dựa vào công cụ nào, và bộ sinh tệp phải chấp nhận những giới hạn gì để hỗ trợ các công cụ ấy? Bằng cách sinh tệp dự án cho công cụ sẵn có thay vì yêu cầu mọi người cùng dùng dòng lệnh, CMake cho thấy giữ được quy trình làm việc hiện có của lập trình viên có thể quan trọng hơn áp đặt một giao diện lý tưởng duy nhất.

Bài liên quan

Đọc bằng ngôn ngữ khác: English

Nguồn