• Thứ Hai, 28/11/2005 14:06 (GMT+7)

    Microsoft Build Engine: Cú nhảy vọt của Visual Studio 2005

    Nếu như việc biên dịch các chương trình thành khối gắn kết (assembly) trong Visual Studio 2003 là một bước đi thành công của Microsoft trong việc đơn giản và chặt chẽ hoá biên dịch ứng dụng thì dường như cơ chế biên dịch mới có tên Microsoft Build Engine (MSBuild) trong Visual Studio 2005 (tên mã Whidbey) sẽ là một cú nhảy vọt. Bài viết sẽ giới thiệu về MSBuild, cách thức để tạo và tích hợp một dự án với Visual Studio 2005.

    Tổng quan

    Thoạt nhìn MSBuild không có gì ấn tượng! Đây là cơ chế biên dịch (build) chứ không phải một ứng dụng cụ thể nên không có giao diện người dùng (GUI). Tất cả các tính năng của MSBuild đều được ẩn giấu bên trong.

    Trước hết ta thử tiếp cận với khả năng cho phép khởi tạo các sự kiện "tiền biên dịch" (Pre-build Event) và "hậu biên dịch" (Post-build event). Với MSbuild, các sự kiện này đều cho phép bạn can thiệp bằng các dòng lệnh tuỳ ý, qua đó bạn có thể kiểm soát việc tự động chạy các ứng dụng từ bên ngoài, thiết lập các biến môi trường... trước và sau biên dịch.

    Với những phiên bản trước của Visual Studio, nội dung của các solution bị bó hẹp trong các tệp .sln của Visual Studio khiến việc tương tác trực tiếp trở nên khó khăn, đặc biệt là việc kiểm soát các sự kiện trên. Nói một cách khác, bạn không thể hiểu được quá trình build diễn ra như thế nào. MSBuild chính là giải pháp cho vấn đề này, nó được tích hợp thẳng vào cửa sổ Solution Explorer giúp cho việc tương tác trở nên đơn giản và tiết kiệm thời gian, công sức.

    Nền tảng của MSBuild

    Thực tế, MSBuild đã được tích hợp một cách chặt chẽ trong nhân của Windows Vista (tên mã Longhorn) và bộ nền .Net Framework của Visual Studio 2005. MSBuild thực tế chỉ là tệp msbuild.exe, một chương trình giao tiếp dòng lệnh không hơn không kém, nhưng ẩn bên trong lớp vỏ kia là cơ chế biên dịch cực mạnh. Sau khi nhận được thông tin đường dẫn của tệp cần biên dịch, nó sẽ chuyển mọi công việc cho phần nhân của MSBuild là MSBuildEngine.dll. Công việc biên dịch phụ thuộc vào tệp dựa trên chuẩn XML với phần mở rộng .proj. Các thay đổi mới được phản ánh ngay trong tệp tin .proj này. Bạn có thể xem qua ví dụ ở Đoạn mã 1.

    Đoạn mã 1 : Ví dụ về tệp tin .proj

    <Project MSBuildVersion="1.0" DefaultTargets = "Compile">

    <Property appname = "Ví_dụ_vê_MSBuild"/>

    <Item Type = "CSFile" Include = "consolehwcs1.cs"/>

    <Target Name = "Compile">

    <Task Name = "CSC" Sources = "@(CSFile)">

    <OutputItem TaskParameter = "OutputAssembly" Type = "EXEFile" Include = "$(appname).exe"/>

    </Task>

    <Message Text="Kết quả quá trình build : @(EXEFile)"/>

    </Target>

    </Project>

    Chúng ta cùng xem xét qua nội dung của tệp tin này để có thể hiểu được phần nào công việc của MSBuild.

    Thẻ Project:
    Tất cả các project đều có một phần tử gốc là project. Thuộc tính DefaultTargets của nó chỉ rõ tên của hành động mà hệ thống sẽ build nếu như bạn không đưa ra. Ở đây thuộc tính có giá trị Compile có nghĩa là biên dịch.

    Thẻ PropertyGroup và Property:
    Sử dụng thẻ Property để xác lập các thuộc tính của việc build. Các thẻ Property có thể ở cấp project - project level, hay cấp khoản mục - item level. Thẻ Properties mặc định có hai thuộc tính: thuộc tính Name nêu tên của Property và thuộc tính Condition tuỳ chọn đề ra các yêu cầu nếu cần. Còn thẻ PropertyGroup sử dụng để nhóm một loạt các thẻ Property lại với nhau.

    Thẻ Item và Item Group:
    Thẻ này định ra các phần được kèm theo khi build và nó có 4 thuộc tính: Type xác định kiểu của tệp tin; Include đưa ra đường dẫn tới tệp tin (thuộc tính này cho phép sử dụng các wildcard, ví vụ *.cs); Exclude ngược lại với Include, nó liệt kê những gì không muốn kèm theo khi build; cuối cùng là Condition, thuộc tính này đưa ra yêu cầu nếu cần. Thẻ ItemGoup tương tự như Item nhưng có tác dụng nhóm nhiều Item lại.

    Thẻ Import:
    Dùng để đưa vào một project có sẵn để dùng mà không cần khai báo lại từ đầu.

    Cơ chế hoạt động

    Linh hoạt và dễ tương tác

    Trước hết mời bạn xem qua đoạn mã 2, ở đây nêu ra cách sử dụng wildcard (tức là dùng "*" để thay cho một nhóm ký tự và dùng "?" để thay cho một ký tự riêng lẻ).

    Đoạn mã 2: Sử dụng wildcard trong tệp tin .proj

    <Project MSBuildVersion="1.0" DefaultTargets = "Compile">

    <Property appname = "Ví_dụ_về_dùng_wildcard"/>

    <Item Type = "CSFile" Include = "*.cs"/>

    <Target Name = "Compile">

    <Task Name = "CSC" Sources = "@(CSFile)">

    <OutputItem TaskParameter = "OutputAssembly" Type = "EXEFile" Include = "$(appname).exe"/>

    </Task>

    <Message Text="Kết quả quá trình build : @(EXEFile)"/>

    </Target>

    </Project>

    Bạn có thể dùng thẻ Item như sau để yêu cầu MSBuild biên dịch kèm các tệp có phần mở rộng là "cs" ở thư mục hiện tại và các tệp có phần mở rộng là "jpg" ở các thư mục con của thư mục Images nằm trong thư mục hiện tại:

    <Item Type="CSFile" Include="*.cs"/>

    <Item Type="CSFile" Include="Images\**\*.jpg"/>

    Ở trên (đoạn mã 1) ta có đề cập đến các thẻ Task. Giờ đây là lúc để bạn thấy rõ tác dụng của nó. Hãy xem đoạn mã 3.

    Đoạn mã 3: Tương tác với hệ thống tệp tin, kiểm tra điều kiện

    <Project MSBuildVersion="1.0" DefaultTargets="Compile">

    <Property appname="Ví_dụ_về_thẻ_Task "/>

    <Property OutDir="bin\"/>

    <Item Type="CSFile" Include="*.cs"/>

    <Target Name="Compile">

    <Task Name="MakeDir" Directories="Bin" Condition="!Exists(Bin)" />

    <Task Name="CSC" Sources="@(CSFile)">

    <OutputItem TaskParameter="OutputAssembly" Type="EXEFile" Include="$(OutDir)$(appname).exe"/>

    </Task>

    <Message Text="Kết quả quá trình build : @(EXEFile)"/>

    <Message Text="Thư mục SecondOutput đã tồn tại..." Condition="Exists(SecondOutput)" />

    <Message Text="Thư mục SecondOutput đã được tạo..." Condition="!Exists(SecondOutput)" />

    <Task Name="MakeDir" Directories="SecondOutput" Condition="!Exists(SecondOutput)" />

    <Task Name="Copy" SourceFiles="@(CSFile)" Destination="SecondOutput" />

    <Message Text="Các tệp tin được copy thành công "/>

    </Target>

    </Project>

    Bạn có thể thấy dòng thứ 6 của đoạn mã này có công dụng tạo ra thư mục mới là thư mục con của thư mục hiện tại và có tên là "Bin" với điều kiện chưa tồn tại thư mục "Bin". Điều này được diễn giải qua thuộc tính Name có giá trị "MakeDir" tức là tạo thư mục mới và thuộc tính Directories có giá trị "Bin" tức là tạo thư mục tên "Bin", cuối cùng là điều kiện để thực thi dòng lệnh đó là thuộc tính Condition với giá trị "!Exist("Bin")" tức là chưa tồn tại thư mục "Bin" (Dấu "!" trong lập trình thường được hiểu là "khác", ví dụ "a != 3" tức là "a khác 3"). Các dòng mã khác tương tự về mặt cú pháp, bạn có thể tham khảo dòng mã 14, nó nhiệm vụ copy "sản phẩm" của quá trình build vào thư mục "SecondOutput". Cuối cùng là dòng 15, dòng này có tác dụng đưa ra một dòng thông báo có nội dung là giá trị thuộc tính Text của thẻ Message.

    Tính tuỳ biến và hỗ trợ cao

    Trước hết nói về tính tùy biến, giả sử bạn cần thực thi một hành động tùy ý khi build. MSBuild cho phép bạn xây dựng các Task của riêng mình – Custom Task. Ví dụ chúng ta có đoạn mã 4, đây là một đoạn mã được viết bằng ngôn ngữ C# và chỉ có một tác dụng là hiện ra hộp thông báo.

    Đoạn mã 4: Task tùy biến

    using System;

    using Microsoft.Build.Utilities;

    namespace MsgTask {

    /// <summary>

    /// Một task tuỳ biến đơn giản có nhiệm vụ hiển thị hộp thông báo

    /// </summary>

    public class MsgTsk: Task

    {

    public bool Execute()

    {

    System.Windows.Forms.MessageBox.Show("Xin chào các bạn đây là một hộp thông báo được hiển thị bởi một task tuỳ biến!");

    return(true);

    }

    }

    }

    Đoạn mã này không có gì đặc biệt cả nhưng hãy thử biên dịch nó (giả sử là "MessageTask.dll") và đưa tệp output vào thư mục CLR (Common Language Runtime). Sau đó đưa đoạn mã sau vào project thử xem:

    <UsingTask TaskName="MsgTask.MsgTsk" AssemblyName="MessageTask "/>

    <Task Name="MsgTsk " />

    Thế là khi bạn build project, một hộp thông báo sẽ hiện ra, thành quả của bạn đó! Thật đơn giản.

    Thế còn hỗ trợ, MSbuild hỗ trợ gì cho bạn? Đó là cơ chế debug (gỡ lỗi) và thú vị hơn là MSBuild cho bạn gỡ lỗi cả những task tùy biến. Bạn hiểu rõ việc viết mã luôn có lỗi, không ít thì nhiều, không bao giờ có thể tránh được vì vậy việc viết ra các task cũng tương tự như viết phần mềm, gỡ lỗi là cần thiết. Thậm chí MSBuild còn cho phép bạn đưa hẳn assembly vào thư mục gốc của .Net Framework để tự động debug bản mới nhất.

    KẾT LUẬN

    Như trên đã nói, MSBuild không chỉ là một cơ chế biên dịch project đơn thuần, MSBuild làm được nhiều điều mà các cơ chế biên dịch khác không làm được.

    Cho dù Visual Studio 2005 vẫn còn ở giai đoạn Beta (hiện tại đã có bản Beta 2) nhưng MSBuild đã được Microsoft dành nhiều công sức để phát triển và chắc sẽ có chắn tương lai tươi sáng. Theo Microsoft, MSBuild không chỉ được tích hợp vào Visual Studio mà còn vào cả các thế hệ hệ điều hành trong tương lai của hãng, điển hình là phiên bản Windows Vista tới đây. Hy vọng với MSBuild, việc biên dịch các ứng dụng trở nên dễ dàng hơn, đơn giản hơn và nhẹ nhàng hơn.

    Nguyễn Trí Trung
    trungnt88@yahoo.com

    -------------------------------------------------------
    Nguồn tham khảo
    1. Overview of MSBuild
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnlong/html/msbuildpart1.asp
    2. Understanding MSBuild in Longhorn
    http://www.c-sharpcorner.com/Longhorn/Longhorn/MSBuild.asp
    3. AKipmans MSBuild blog
    http://blogs.msdn.com/akipman/default.aspx

    ID: A0511_118