Trang này thảo luận về hệ thống xây dựng, chức năng của hệ thống xây dựng, lý do bạn nên sử dụng hệ thống xây dựng và lý do trình biên dịch và tập lệnh bản dựng không phải là lựa chọn tốt nhất khi tổ chức của bạn bắt đầu mở rộng quy mô. Tài liệu này dành cho những nhà phát triển không có nhiều kinh nghiệm về hệ thống xây dựng.
Hệ thống xây dựng là gì?
Về cơ bản, tất cả hệ thống xây dựng đều có một mục đích đơn giản: chuyển đổi mã nguồn do kỹ sư viết thành tệp nhị phân có thể thực thi mà máy có thể đọc. Hệ thống xây dựng không chỉ dành cho mã do con người tạo; mà còn cho phép máy tự động tạo bản dựng, cho dù là để kiểm thử hay phát hành công khai. Trong một tổ chức có hàng nghìn kỹ sư, thông thường, hầu hết các bản dựng đều được kích hoạt tự động thay vì do kỹ sư trực tiếp kích hoạt.
Tôi không thể chỉ sử dụng trình biên dịch được sao?
Bạn có thể không nhận ra ngay nhu cầu về một hệ thống xây dựng. Hầu hết các kỹ sư không sử dụng hệ thống xây dựng trong khi học lập trình: hầu hết bắt đầu bằng cách gọi các công cụ như gcc
hoặc javac
trực tiếp từ dòng lệnh hoặc tương đương trong môi trường phát triển tích hợp (IDE). Chỉ cần tất cả mã nguồn đều nằm trong cùng một thư mục, thì lệnh như sau sẽ hoạt động tốt:
javac *.java
Thao tác này sẽ hướng dẫn trình biên dịch Java lấy mọi tệp nguồn Java trong thư mục hiện tại và biến tệp đó thành tệp lớp nhị phân. Trong trường hợp đơn giản nhất, đây là tất cả những gì bạn cần.
Tuy nhiên, ngay khi mã mở rộng, các chức năng sẽ bắt đầu. javac
đủ thông minh để tìm trong các thư mục con của thư mục hiện tại để tìm mã cần nhập. Nhưng trình tìm kiếm không có cách nào tìm thấy mã được lưu trữ trong các phần khác của hệ thống tệp (có thể là một thư viện do nhiều dự án chia sẻ). Công cụ này cũng chỉ biết cách tạo mã Java. Các hệ thống lớn thường bao gồm nhiều phần được viết bằng nhiều ngôn ngữ lập trình với mạng lưới các phần phụ thuộc giữa các phần đó, nghĩa là không có trình biên dịch nào cho một ngôn ngữ có thể tạo ra toàn bộ hệ thống.
Khi bạn xử lý mã từ nhiều ngôn ngữ hoặc nhiều đơn vị biên dịch, việc tạo mã không còn là một quy trình một bước nữa. Bây giờ, bạn phải đánh giá những phần phụ thuộc vào mã của mình và tạo các phần đó theo thứ tự thích hợp, có thể sử dụng một bộ công cụ khác nhau cho mỗi phần. Nếu có phần phụ thuộc nào thay đổi, bạn phải lặp lại quy trình này để tránh phụ thuộc vào các tệp nhị phân cũ. Đối với cơ sở mã có kích thước trung bình, quá trình này nhanh chóng trở nên tẻ nhạt và dễ gặp lỗi.
Trình biên dịch cũng không biết gì về cách xử lý các phần phụ thuộc bên ngoài, chẳng hạn như tệp JAR
của bên thứ ba trong Java. Nếu không có hệ thống xây dựng, bạn có thể quản lý việc này bằng cách tải phần phụ thuộc xuống từ Internet, gắn phần phụ thuộc đó vào thư mục lib
trên ổ đĩa cứng và định cấu hình trình biên dịch để đọc thư viện từ thư mục đó. Theo thời gian, bạn sẽ khó duy trì các bản cập nhật, phiên bản và nguồn của các phần phụ thuộc bên ngoài này.
Còn tập lệnh shell thì sao?
Giả sử dự án mà bạn làm theo sở thích bắt đầu khá đơn giản để bạn có thể tạo dự án đó chỉ bằng một trình biên dịch, nhưng bạn bắt đầu gặp phải một số vấn đề đã mô tả trước đó. Có thể bạn vẫn không nghĩ mình cần một hệ thống xây dựng và có thể tự động hoá các phần tẻ nhạt bằng cách sử dụng một số tập lệnh shell đơn giản để xử lý việc tạo các phần theo đúng thứ tự. Điều này giúp ích trong một thời gian, nhưng chẳng bao lâu bạn sẽ bắt đầu gặp phải nhiều vấn đề hơn:
Việc này trở nên tẻ nhạt. Khi hệ thống của bạn trở nên phức tạp hơn, bạn bắt đầu dành gần như nhiều thời gian cho việc xử lý tập lệnh bản dựng như dành cho mã thực. Việc gỡ lỗi tập lệnh shell rất khó khăn, với ngày càng nhiều bản hack được xếp chồng lên nhau.
Chạy chậm. Để đảm bảo bạn không vô tình dựa vào các thư viện cũ, bạn có tập lệnh bản dựng tạo mọi phần phụ thuộc theo thứ tự mỗi khi chạy tập lệnh đó. Bạn nghĩ đến việc thêm một số logic để phát hiện phần nào cần được tạo lại, nhưng điều đó nghe có vẻ rất phức tạp và dễ xảy ra lỗi đối với một tập lệnh. Hoặc bạn nghĩ đến việc chỉ định những phần cần tạo lại mỗi lần, nhưng sau đó bạn lại quay lại vạch xuất phát.
Tin vui là đã đến lúc phát hành! Tốt hơn hết, bạn nên tìm hiểu tất cả đối số mà bạn cần truyền vào lệnh jar để tạo bản dựng cuối cùng. Và hãy nhớ cách tải lên và đẩy tệp đó vào kho lưu trữ trung tâm. Đồng thời, hãy tạo và đẩy nội dung cập nhật tài liệu cũng như gửi thông báo cho người dùng. Hmm, có lẽ cần phải có một tập lệnh khác...
Thảm hoạ! Ổ đĩa cứng của bạn gặp sự cố và giờ đây, bạn cần tạo lại toàn bộ hệ thống. Bạn đã đủ thông minh để giữ tất cả tệp nguồn trong chế độ kiểm soát phiên bản, nhưng còn những thư viện bạn đã tải xuống thì sao? Bạn có thể tìm lại tất cả các tệp đó và đảm bảo rằng chúng có cùng phiên bản như khi bạn tải xuống lần đầu không? Các tập lệnh của bạn có thể phụ thuộc vào các công cụ cụ thể được cài đặt ở những vị trí cụ thể. Bạn có thể khôi phục chính môi trường đó để các tập lệnh hoạt động trở lại không? Còn tất cả các biến môi trường mà bạn đã đặt từ lâu để trình biên dịch hoạt động đúng cách nhưng sau đó lại quên mất?
Mặc dù gặp vấn đề, nhưng dự án của bạn đã thành công đến mức bạn có thể bắt đầu tuyển dụng thêm kỹ sư. Giờ đây, bạn nhận ra rằng không cần phải có sự cố nào xảy ra thì các vấn đề trước đó mới phát sinh. Bạn cần phải trải qua cùng một quy trình khởi động đau đớn mỗi khi có một nhà phát triển mới gia nhập nhóm. Và mặc dù bạn đã nỗ lực hết sức, nhưng vẫn có một số khác biệt nhỏ trong hệ thống của mỗi người. Thông thường, những gì hoạt động trên máy của người này sẽ không hoạt động trên máy của người khác, và mỗi lần sẽ mất vài giờ để gỡ lỗi đường dẫn công cụ hoặc phiên bản thư viện để tìm ra sự khác biệt.
Bạn quyết định cần tự động hoá hệ thống xây dựng. Về lý thuyết, việc này đơn giản như việc mua một máy tính mới và thiết lập máy tính đó để chạy tập lệnh bản dựng mỗi đêm bằng cron. Bạn vẫn cần phải trải qua quy trình thiết lập rườm rà, nhưng giờ đây, bạn không có lợi ích của bộ não con người có thể phát hiện và giải quyết các vấn đề nhỏ. Giờ đây, mỗi sáng khi vào công việc, bạn sẽ thấy bản dựng đêm qua không thành công vì hôm qua, một nhà phát triển đã thực hiện một thay đổi hoạt động trên hệ thống của họ nhưng không hoạt động trên hệ thống bản dựng tự động. Mỗi lần chỉ là một cách khắc phục đơn giản, nhưng điều này xảy ra thường xuyên đến mức bạn phải dành nhiều thời gian mỗi ngày để khám phá và áp dụng những cách khắc phục đơn giản này.
Các bản dựng ngày càng chậm hơn khi dự án phát triển. Một ngày nọ, trong khi chờ một bản dựng hoàn tất, bạn nhìn chăm chú vào màn hình máy tính đang ở trạng thái rảnh của đồng nghiệp đang đi nghỉ phép và ước gì có cách nào để tận dụng tất cả sức mạnh tính toán bị lãng phí đó.
Bạn đã gặp phải một vấn đề kinh điển về quy mô. Đối với một nhà phát triển làm việc trên tối đa vài trăm dòng mã trong tối đa một hoặc hai tuần (có thể là toàn bộ kinh nghiệm cho đến nay của một nhà phát triển mới tốt nghiệp đại học), bạn chỉ cần một trình biên dịch. Tập lệnh có thể giúp bạn đi xa hơn một chút. Tuy nhiên, ngay khi bạn cần điều phối giữa nhiều nhà phát triển và máy của họ, ngay cả một tập lệnh bản dựng hoàn hảo cũng không đủ vì rất khó để tính đến những khác biệt nhỏ trong các máy đó. Tại thời điểm này, phương pháp đơn giản này sẽ không còn hiệu quả và đã đến lúc bạn đầu tư vào một hệ thống xây dựng thực sự.