Showing posts with label MSBuild. Show all posts
Showing posts with label MSBuild. Show all posts

Monday, April 6, 2015

MSBuild 시작하기

MSBuild란 무엇인가? MSBuild란 오프라인, 그러니까 콘솔에서 Visual Studio IDE에서 수행했던 컴파일과 링크, 빌드를 수행해 주게 하는 프로그램이다. 유닉스에서 GNU Make, 자바의 Ant와 비슷하다. (금융권에 근무하던 시절 Makefile을 수천번 수정하고, 자바의 Ant로 자동빌드를 수행하던 기억이 난다.)

사실 처음에 MSBuild를 접하고 공부하였을 때는 Visual Studio는 JDK와 Eclipse처럼 IDE와 SDK가 분리되지 않는다고 생각했었다. 그러나 이것도 예전에 어렴풋이 알고 있던 cl.exe, al.exe, ln.exe와 같이 빌드시스템도 마찬가지로 분리되어 있었다.

Visual Studio에서 무언가를 컴파일 할려면 일반적으로 IDE(즉, Visual Studio GUI 프로그램)을 열어서 컴파일을 해야한다. 그런데 솔루션에 포함된 프로젝트가 정말 많고, 일부만 변해도 전체를 다 읽어서 컴파일을 할 것인가? 이것은 대규모 프로젝트에서는 IDE를 로딩하고 솔루션을 로딩하는데에 상당한 시간을 할애하게 되어, 누적되면 엄청난 시간의 손실을 가져온다.

또한, 그래피컬하게 프로젝트의 프로퍼티 페이지를 수정하는 것의 이점도 있지만, 직접 MSBuild 스크립트를 수정하여 사용하게 되면 조건에 따른 컴파일, 프로젝트 설정간의 상속도 가능하며, 중앙집중식 관리도 가능하다. MSBuild 스크립트는 csproj(C#), vbproj(VB.NET)와 같이 .NET 언어의 프로젝트 파일을 말하는데, 열어보면 알겠지만 XML이다.

암튼, 그래서 배우게 된 MSBuild. 이름도 많이 들어보고 인터넷 상에 자료도 많고 MSDN에 자료도 많지만 정작 가장 simple한 example이 없어서 직접 만들어 보았다. 언어는 C#이며, csproj에 .NET Framework 2.0의 MSBuild를사용한다.

MSBuild.exe는 C:\Windows\Microsoft.NET\Framework\v2.0.50727에 있다.
그리고 C#컴파일러인 csc.exe도 같은 폴더에 존재한다.

다음은 간단한 예제 C#소스이다. 사실 아무것도 없는 Hello World이다.
consolehwcs1.cs:
using System;
/// <summary>
/// Summary description for Class1
/// </summary>
public class ConsoleHWCS1
{
    public static void Main()
 {
  //
  // TODO: Add constructor logic here
  //
        Console.WriteLine("ConsoleHWCS1");
 }
}

다음은 우리가 살펴볼 예제 msbuild스크립트이다.(확장자가 csproj이다.)
MSBuildTest.csproj:
// 디폴트 타겟은 "Compile"이다.
<Project DefaultTargets = "Compile"
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
// 프로퍼티 그룹, 여기에 정의된 <tag>들은 $(tag)이렇게 사용할 수 있다. 상수와 같다.
    <PropertyGroup>
// 어플리케이션 이름 
        <!-- Set the application name as a property -->
        <appname>HelloWorldCS</appname><!-- exe output filename -->
// 출력 폴더 이름
        <!-- Set the output folder as a property -->
        <builtdir>BuiltApp</builtdir>
    </PropertyGroup>

    <ItemGroup>
// 여기는 컴파일될 소스들의 리스트를 적는다.
        <!-- Specify the inputs by type and file name -->
        <CSFile Include = "consolehwcs1.cs"/>
    </ItemGroup>

// 여기는 컴파일 타겟을 정의 한다.
    <Target Name = "Compile">
        <!-- Check whether an output folder exists and create
        one if necessary -->
// 빌드디렉토리가 없을 경우(컨디션 적용), 디렉토리를 만든다. 이런것도 가능하다. 원래 Visual Studio의 기능이니
        <MakeDir Directories = "$(builtdir)" 
            Condition = "!Exists('$(builtdir)')" />

// 컴파일러를 호출해서 컴파일을 수행한다.
// 출력파일(어셈블리)이름도 정의해 준다.
// 그리고 소스들은 멀티로 적용한다. @(CSFIle)
        <!-- Run the Visual C# compiler -->
        <CSC Sources = "@(CSFile)" 
            OutputAssembly = "$(BuiltDir)\$(appname).exe">
            <Output TaskParameter = "OutputAssembly"
                ItemName = "EXEFile" />
        </CSC>

// 빌드가 끝나고 화면에 메세지를 뿌려준다. 위에서 정의한 OutputAssembly를 EXEFile에 보존했다가 화면에 뿌려줌
        <!-- Log the file name of the output file -->
        <Message Text="The output file is @(EXEFile)"/>
    </Target>
// 유닉스에서 Make clean과 동일하다. 전체 지우고 리빌드 할 경우에 사용

    <Target Name = "Clean">
        <RemoveDir Directories="$(builtdir)" />
    </Target>
</Project>


다음과 같이 입력후에 수행을 하면 빌드가 된다.
C:\Windows\Microsoft.NET\Framework\v2.0.50727\msbuild msbuildtest.csproj /t:Compile

/t:Compile은 컴파일 타겟이다. 유닉스에서 Make TargetName을 하는 것과 유사하다.
그러면 다음과 같이 메세지가 나오면서 빌드이 될 것이고, 결과 실행파일인 HelloWorldCS.exe는 .\BuildApp에 들어가게 된다.


이상으로 첫 MSBuild 프로그램이 완성 되었다. 끝으로, csproj파일을 Visual Studio에서 로딩을해 보면 로딩이 될 것이다.  

Friday, April 3, 2015

MSBuild Registry 읽기

다음와 같이 하면 MSBuild XML 파일에서 Registry 값을 읽어올 수 있다.

<PropertyGroup>
<MyProperty>$(registry:HKEY_LOCAL_MACHINE\MyKey\MySubKey@ValueName)</MyProperty>
</PropertyGroup>

MSBuild Build와 Clean를 커스터마이즈 하기

Target중에서 외부에서 사용하는 것은 Build, Rebuild, Clean이 있는데 이것을 커스터마이즈하는 가장 쉬운 방법은 BuildDependsOn, RebuildDependsOn, CleanDependsOn이라는 property를 다음과 같이 수정해 주면된다.

아래의 예제에서는 전체 BuildDependsOn Target을 수행하기전에 DependsBuild라는 Target을 먼저 수행한후에 하라는 예제이다. DependsBuild는 물론 <Target Name="DependsBuild">로 정의되어야 한다.

   <PropertyGroup>
        <BuildDependsOn>DependsBuild;$(BuildDependsOn);</BuildDependsOn>
        <CleanDependsOn>DependsClean;$(CleanDependsOn);</CleanDependsOn>
    </PropertyGroup>

내부의 Build과정에 대해서는 수많은 복잡한 Import되는 MSBuild에서 제공하는 props와 targets가 있지만 다음번에 다루도록 하겠다.

MSBuild Property Functions 호출하기

MSBuild 4.0에서는 프로퍼티(그러니까 문자열)에 대해서 함수를 호출하여 특정 기능을 수행할수 있다.
예를 들어 문자열을 substring한다던가 하는 기능을 task를 따로 만들지 않고서도 가능하다.
특정 네임스페이스에서 호출할 때는 다음과 같이 한다.
$([Namespace.Type]::Method(..parameters…))
$([Namespace.Type]::Property)
$([Namespace.Type]::set_Property(value))

그리고 특정 프로퍼티에 대한 호출은 다음과 같이 한다.
$(property.Method(..parameters...))
$(property.Property)
$(property.set_Property(value))

http://blogs.msdn.com/b/visualstudio/archive/2010/04/02/msbuild-property-functions.aspx

MSBuild VCTargetPath, MSBuildToolsPath 프로퍼티

MSBuild 4.0에서 C++용 MSBuild 스크립트를 다루다 보면 다음 두가지의 프로퍼티 변수가 자주 나온다. 예를들어 Microsoft.Cpp.targets를 import할때 $(VCTargetPath)에서 import하는데 그 프로퍼티는 레지스트리에 저장되어 있으며 그 값은 다음과 같다.

이렇게 분리한 이유는 VCTargetPath는 Visual Studio 2010을 설치하면 생기는 C++전용 targets 파일들이고, MSBuildToolsPath에는 C++,C#등 공통적은 targets가 들어있다. 이 프로퍼티들은 import되는 targets를 분석하는데에는 알고있는 것이 유용하다. 왜냐하면 소스를 볼 일이 생길수도 있다.

$(VCTargetPath)
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0

$(MSBuildToolsPath)
C:\Windows\Microsoft.NET\Framework\v4.0.30319

MSBuild 파일에서 C#을 인라인 스크립트로 사용하기

정의:
먼저 Task를 다음과 같이 정의한다. 어셈블리 파일은 새로 생성되는 파일이 아니라 기존에 존재하는 파일이다. 스크립트 엔진으로서 Task를 상속받기위해 사용한다.

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This simple inline task displays "Hello, world!" -->
  <UsingTask
    TaskName="HelloWorld"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
    <ParameterGroup />
    <Task>
      <Reference Include="System.Xml.dll"/>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
<![CDATA[
// Display "Hello, world!"
Log.LogError("Hello, world!");
]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

사용:
사용할때는 일반 Task와 동일하게 사용하면 된다.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="HelloWorld.targets" />
  <Target Name="Hello">
    <HelloWorld />
  </Target>
</Project>

MSBuild C++ 예제 2/2

Target을 직접 만들지 않고 import하여 사용하는 MSBuild C++ 샘플 #2
IDE에서 기본적으로 사용하는 Build/Rebuild/Clean Target을 사용한다.

<Project DefaultTargets="Build" ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.default.props" />
  <PropertyGroup>
    <ConfigurationType>Application</ConfigurationType>
    <ShowAllFiles>false</ShowAllFiles>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)/Microsoft.Cpp.props" />
  <ItemGroup>
    <ClCompile Include="main.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="main.h" />
  </ItemGroup>  
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Targets" />
</Project>

MSBuild C++ 예제 1/2

Target을 import하지 않고 직접 만들어서 CL Task를 사용한 MSBuild C++ 샘플
MSBuild C++은 Visual Studio 2010에서만 동작한다.

<Project DefaultTargets="Build" ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
             <Import Project="$(VCTargetsPath)\Microsoft.Cpp.default.props" />
             <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Targets" />
             <ItemGroup>
                           <ClCompile Include="main.cpp" />
             </ItemGroup>
             <Target Name="Build">
                           <Cl Sources="@(ClCompile)">
                                        <Output TaskParameter="ObjectFiles" ItemName="Objects" />
                           </Cl>
                           <Link Sources="@(Objects)"/>
                           <Message Text="The output file is @(Objects)" />
             </Target>
</Project>