Là một nhà phát triển PostgreSQL, đôi khi tôi cần làm cho mã của mình hoạt động trên Windows. Vì tôi thường không sử dụng Windows và không có cài đặt Windows vĩnh viễn nên việc này luôn hơi phức tạp. Tôi đã phát triển một số kỹ thuật để giúp việc này trở nên dễ dàng hơn và tôi thấy chúng đáng được chia sẻ. Và thực sự văn bản này khá dài, vì vậy đây sẽ là một loạt các bài đăng trên blog.
Điều hữu ích đầu tiên cần hiểu là các mục tiêu xây dựng Windows biến thể khác nhau và chúng giống và khác nhau như thế nào.
Có thể cho là cách chính để xây dựng cho Windows là sử dụng Microsoft Visual Studio bộ biên dịch. Đây là những gì bạn có thể coi là môi trường bản địa nhất. Hầu hết nếu không phải tất cả các bản phân phối nhị phân của PostgreSQL cho Windows đều sử dụng bản dựng này. Bản dựng này không sử dụng các makefiles Unix bình thường mà là một hệ thống xây dựng riêng trong src/tools/msvc/
. Điều này phân tích cú pháp các tệp makefiles và có một số logic tùy chỉnh và xây dựng các tệp “dự án” cụ thể cho chuỗi công cụ này, sau đó bạn có thể chạy để xây dựng mã. Hãy gọi đây là bản dựng MSVC ở đây. Bản dựng này dễ bị hỏng theo hai cách:Một, nếu mã không thực sự tạo hoặc chạy trên Windows, và hai, nếu bạn thay đổi điều gì đó trong hệ thống xây dựng bình thường (dựa trên tệp) khiến các tập lệnh đặc biệt đó phá vỡ. Vì vậy, điều này luôn có rất nhiều điều thú vị để giải quyết.
Cách thứ hai là sử dụng MinGW . Điều này sử dụng chuỗi công cụ GNU (GCC, GNU binutils, GNU ld, v.v.) để xây dựng mã gốc trên Windows. Bạn có thể coi nó là “GCC trên Windows”, nhưng trên thực tế, nó chứa thêm miếng đệm và keo để giao tiếp với các thư viện hệ thống Windows. Điều này sử dụng hệ thống xây dựng Unix-ish bình thường bằng cách sử dụng cấu hình và tệp tạo, nhưng mã mà nó tạo ra là mã gốc của Windows, về nguyên tắc tương đương với đầu ra của bản dựng MSVC. Điều này cũng có nghĩa là nếu mã không được tạo hoặc chạy với bản dựng MSVC, thì nó cũng sẽ không được tạo hoặc chạy ở đây. Nhưng hệ thống xây dựng cũng giống như hệ điều hành Linux, v.v., do đó, việc vô tình bị hỏng sẽ khó hơn.
Cách thứ ba là Cygwin . Cygwin là một hệ thống con thể hiện một môi trường giống như POSIX trên Windows. Ví dụ:Cygwin thêm người dùng, nhóm, fork()
, Bộ nhớ chia sẻ SysV và các tiện ích khác không tồn tại trên Windows gốc nhưng là tiêu chuẩn trên Linux. Ý tưởng là bạn có thể lấy mã nguồn dành cho Linux hoặc BSD và xây dựng nó theo Cygwin mà không cần thay đổi (hoặc ít nhất là chỉ với những thay đổi nằm trong phạm vi thay đổi cổng điển hình giữa các hệ thống giống Unix). Vì lý do này, một cổng Cygwin của PostgreSQL đã tồn tại rất lâu trước cổng Windows gốc, bởi vì nó là một nỗ lực nhỏ hơn nhiều. Trong thực tế, sự trừu tượng bị phá vỡ trong một số lĩnh vực, đặc biệt là trong mã mạng và xung quanh việc đặt tên và truy cập tệp, nhưng nói chung, bản dựng Cygwin rất hiếm khi bị phá vỡ so với các mục tiêu khác.
Đã từng có một cách khác để xây dựng trên Windows. Đã có win32.mak
các tệp mà bạn có thể sử dụng trực tiếp với nmake trên Windows và cũng có hỗ trợ cho các trình biên dịch Borland tại một số điểm. Về cơ bản, đây là các biện pháp stopgap để chỉ xây dựng libpq nguyên bản trên Windows trước khi cổng gốc đầy đủ xuất hiện. Chúng hiện đã bị xóa.
Có một thuật ngữ khác xuất hiện trong ngữ cảnh này: MSYS . Lý do cho điều này là bản thân MinGW thường không hữu ích. Nó chỉ là một chuỗi công cụ biên dịch. Nhưng để xây dựng phần mềm Unix điển hình, bạn cần các công cụ bổ sung như bash, make, sed, grep và tất cả những thứ thường được sử dụng từ tập lệnh cấu hình hoặc tệp trang. Không phải tất cả các công cụ này đều tồn tại dưới dạng các cổng Windows gốc. Nhưng bạn có thể chạy chúng trên hệ thống con Cygwin. Vì vậy, một cách để sử dụng MinGW là từ bên trong Cygwin. Một loại khác là MSYS, viết tắt của “hệ thống tối thiểu”, nói một cách đại khái là một tập hợp con của Cygwin được đặt làm riêng và một số bao bì đặc biệt để sử dụng MinGW cho xây dựng phần mềm. MSYS ban đầu hiện đã bị bỏ theo như tôi biết, nhưng có một MSYS2 mới thay thế phổ biến. Thêm về điều này trong một bài đăng blog tiếp theo. Bây giờ chỉ cần hiểu mối quan hệ giữa tất cả các gói phần mềm khác nhau này.
Bây giờ, hãy xem xét cách mã nguồn nhìn thấy các môi trường xây dựng khác nhau này.
Một bản dựng gốc sử dụng MSVC hoặc MinGW xác định _WIN32
. (Thật kỳ lạ, đây là trường hợp cho cả bản dựng 32 bit và 64 bit. Bản dựng 64 bit cũng định nghĩa _WIN64
, nhưng điều này hiếm khi được sử dụng.) Mã nguồn PostgreSQL sử dụng WIN32
thay vào đó, nhưng điều đó dành riêng cho PostgreSQL, không được thực hiện bởi trình biên dịch.
MSVC cũng định nghĩa _MSC_VER
đến một số phiên bản. Điều này đôi khi hữu ích để giải quyết các vấn đề với một phiên bản trình biên dịch cụ thể (thường là loại mà các bản dựng Unix có xu hướng sử dụng cấu hình cho). Lưu ý rằng MinGW không xác định _MSC_VER
, vì vậy mã cần được viết cẩn thận để xử lý điều đó. Có một số lỗi nhỏ xung quanh vấn đề này vì mã như #if _MSC_VER < NNNN
để có thể giải quyết vấn đề với trình biên dịch cũ hơn cũng sẽ kích hoạt trên MinGW, điều này có thể không được dự kiến. (Đúng hơn sẽ là #if defined(_MSC_VER) && _MSC_VER < NNNN
và tất nhiên là bọc thành #ifdef WIN32
.) MinGW định nghĩa __MINGW32__
và __MINGW64__
, nhưng chúng rất hiếm khi được sử dụng. Ngoài ra, MinGW tất nhiên định nghĩa __GNUC__
vì đó là GCC, vì vậy mã có điều kiện dành riêng cho GCC hoặc phiên bản GCC cũng có thể được sử dụng. Nói chung, vì MinGW sử dụng Autoconf nên những thứ đó thường phải được kiểm tra trong configure
thay vì trong mã.
Cygwin định nghĩa __CYGWIN__
. Đáng chú ý, Cygwin không định nghĩa _WIN32
hoặc WIN32
, v.v. - bởi vì nó không tự coi mình là Windows gốc. Đó là lý do tại sao trong một số vùng mã mà Windows xem qua phần trừu tượng Cygwin, bạn thấy rất nhiều mã có #if defined(WIN32) ||
để xử lý cả hai trường hợp.
defined(__CYGWIN__)
(Có một số góc nhỏ trong mã không phải lúc nào cũng xử lý tất cả các định nghĩa của bộ tiền xử lý này theo cách hợp lý và nhất quán. Trong một số trường hợp, điều này là cố ý vì thực tế là kỳ lạ, trong các trường hợp khác, đó là mã hỏng và không chính xác cần được dọn dẹp.)
Về nguyên tắc, mỗi mục tiêu này tồn tại dưới dạng biến thể 32 bit và 64 bit. Cài đặt hệ điều hành Windows 64 bit, là cài đặt hiện đại thông thường, có thể chạy cả phần mềm 32 bit và 64 bit, vì vậy bạn có thể cài đặt và chạy cả hai biến thể trên một hệ thống như vậy. Cài đặt sản xuất có thể nên sử dụng bản dựng 64-bit và vì vậy bạn có thể chọn không bận tâm đến môi trường 32-bit. Trên thực tế, biến thể 32-bit của Cygwin dường như đã chết và bạn có thể không thể làm cho nó hoạt động được. Tuy nhiên, có một vấn đề là MinGW 64-bit có một số lỗi khó hiểu, vì vậy, khi sử dụng MinGW, đặc biệt là đôi khi tốt hơn nên sử dụng môi trường 32-bit, trừ khi bạn muốn chống lại các lỗi hệ điều hành hoặc chuỗi công cụ. Tuy nhiên, máy tính 32-bit rõ ràng là đang trên đà phát triển nói chung, vì vậy đây không phải là một lựa chọn phù hợp với tương lai.
Bây giờ câu hỏi có lẽ là môi trường nào trong số những môi trường này là “tốt nhất”. Đối với quá trình phát triển, điều đó không thực sự quan trọng vì tất cả mã cần hoạt động trên tất cả chúng. Như tôi đã đề cập ở trên, bản dựng MSVC được sử dụng cho hầu hết các triển khai sản xuất của Windows. Môi trường MinGW (hoặc đúng hơn là MSYS2) sẽ tốt hơn để phát triển nếu bạn đã quen với môi trường giống Unix, nhưng đặc biệt là môi trường MinGW 64 bit có vẻ hơi lỗi, vì vậy rất khó để đề xuất điều này cho quá trình sản xuất. Cygwin có thể được một số người coi là một sự tò mò lịch sử vào thời điểm này. Việc chạy máy chủ sản xuất dưới Cygwin không được khuyến khích vì hiệu suất khá tệ. Nhưng Cygwin thực sự hữu ích trong một số tình huống. Ví dụ:Readline không hoạt động trên một trong hai bản dựng Windows gốc, nhưng nó hoạt động trên Cygwin, vì vậy nếu bạn là người dùng psql, tốt hơn nên sử dụng bản dựng Cygwin cho việc đó. Ngoài ra, Cygwin rất hữu ích trong tình huống ngược lại với bài đăng trên blog này:Bạn là nhà phát triển chủ yếu sử dụng Windows và muốn đảm bảo mã của bạn hầu hết tương thích với Unix. Vì vậy, cả ba môi trường này đều có giá trị của chúng và đáng được duy trì tại thời điểm này.
Trong phần tiếp theo của loạt bài này, tôi sẽ thảo luận một số kỹ thuật để kiểm tra các thay đổi mã trên và cho Windows nếu đó không phải là môi trường phát triển chính của bạn.