'Project Dependencies'에 해당되는 글 2건

  1. 2010.05.10 MSBuild Task : Project Dependencies
  2. 2010.05.05 프로젝트 참조 VS 어셈블리 참조

프로젝트 개수가 많아지다 보면 솔루션 파일을 열어 Visual Studio상에서 빌드를 하는 것보다는 MSBuild를 사용하여 빌드를 하는 것이 현명한 선택일 것입니다. MSBuild를 사용할 경우에 단순히 솔루션 파일을 지정해주면 빌드가 정상적으로 완료되지 않습니다. 바로 프로젝트 간에 참조 문제 때문입니다. MSBuild에서는 파라미터로 솔루션 파일을 지정해주면 해당 솔루션 파일에 정의된 프로젝트 순서대로 빌드 순서를 정해버립니다.
아래의 사이트는 이러한 문제점을 해결해줍니다.
http://serialize.wordpress.com/2009/12/16/msbuild-task/

처음 MSBuild를 접하시는 분은 위의 사이트를 눈을 크게 뜨고 보아도 실행이 잘되지 않을 것입니다.(제가 그랬습니다. ㅋ)
<Project DefaultTargets="BuildProjects" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

 

  <Target Name="BuildProjects">

 

    <GetProjecsInOrder Solution="$(MSBuildProjectDirectory)\MySolution.sln">

      <Output ItemName="ProjectFiles" TaskParameter="Output"/>

    </GetProjecsInOrder>

   

    <Message Text="%(ProjectFiles.FullPath)"/>

   

    <MSBuild Projects="%(ProjectFiles.FullPath)"

      Targets="Build"

      Properties="Configuration=Release"/>

  </Target>

 

  <UsingTask AssemblyFile="MSBuildTasks.dll" TaskName="GetProjecsInOrder" />

 

</Project>

=> MSBuildTasks.dll이라는 사용하는 구문을 몰라서였습니다. -_- MSBuildTasks.dll소스를 열어보면 Task라는 클래스(빌드가 되는 단위 ITaskItem이 프로젝트와 매칭되는 빌드의 단위)를 상속받아서 구현하고 있는 것을 볼 수 있습니다. 솔루션 파일를 정규식으로 필터링을 해서 프로젝트의 정보를 얻습니다. 이 프로젝트 정보로 해당 프로젝트 파일를 열어 프로젝트 파일에 있는 프로젝트 참조에 대한 정보를 정규식을 통해서 얻습니다. 이렇게 모든 정보를 얻고 나서 Recusive한 알고리즘(?!)을 통해서 빌드되는 프로젝트의 순서를 정해주게 됩니다. 위의 Message 앨리먼트를 사용하게 되면 이러한 빌드 순서에 대해서 화면에 찍어주게 됩니다.
한 가지 아쉬운 점이라면 바로 프로젝트 참조에 대해서만 빌드 순서를 정해준다는 것입니다. 물론 일반적인 솔루션이라면 당연히 프로젝트 참조 대해서만 빌드 순서를 정해주면 되지만 모든지 예외는 존재하는 것 같습니다. 그닥 어렵지는 않습니다. 이전 포스트에서 소개해드린 솔루션 파일에 어셈블리 참조에 대한 Project Dependencies 정보가 사전에 필요합니다. 그리고 이 정보를 사용하여 어셈블리 참조에 대한 정보를 얻어 위에서 사용한 Recursive한 루틴을 태우면 됩니다. 프로젝트 참조와 어셈블리 참조 모두를 적용하면 어떻게 될지도 궁금하네요 ^^

<2010-05-12>
MSBuild에서 솔루션 파일안에 프로젝트 파일의 정보 순서대로 빌드를 해준다고 생각했었는데 다시 해보니 Project Dependency에 의해서 빌드 순서를 정해서 빌드를 합니다. 위의 사이트의 해결방법이 그렇다면 쓸모없는 방법이라는 결론입니다. 하지만 700정도 되는 프로젝트를 단순히 솔루션 파일에 의해서 MSBuild를 하면 제대로 빌드가 되지 않습니다. 무엇인가 정확하게 알지 못하는 부분이 있는 것 같아 정확하게 원인이 파악되면 다시 글을 수정하도록 하겠습니다.

<2010-05-23>
Project Dependency를 나타내는 방법은 아래의 두 가지입니다.
1. 프로젝트 참조를 한다.
2. Project Dependencies 메뉴에서 수동으로 Dependency를 정의한다.
2번 째 방법은 솔루션 파일에 ProjectSection(ProjectDependencies)에서 찾아볼 수 있지만 1번 째 방법은 그렇지 않습니다.

MSBuild시에 솔루션 파일을 지정하고 빌드할 경우 테스트해 본 결과 1번째 2번째 방법 모두 빌드 오더를 MSBuild 자체적으로 계산하여 정확하게 빌드를 해주는 것을 확인하였습니다. 위의 사이트의 해결방법이 쓸모없는 방법인 것으로 판단됩니다. --;
Posted by resisa
,

현재 프로젝트에서 참조를 어떻게 하고 계십니까? 아마도 대부분 이렇게 구성하고 계실 것입니다.
1. 프로젝트 사이에 참조가 필요한 경우에는 프로젝트 참조를 한다.
2. 3Party DLL 등등은 어셈블리 참조를 한다.
2번에서 3Party DLL의 소스가 있을 경우에 소스의 수정 여부에 따라서 프로젝트 참조를 할 것입니다.
이렇게만 구성을 하여도 충분히 훌륭한 솔루션(.sln)을 구성한 것입니다.

그렇다면 혹시 프로젝트 참조와 어셈블리 참조의 차이점은 알고 계십니까?
참조의 관점에서 본다면 차이점이 없습니다. 어셈블리 참조는 해당 위치(Path)에 있는 어셈블리를 참조하는 것이고 프로젝트 참조는 프로젝트의 출력 경로의 어셈블리를 참조하게 해주는 것 뿐입니다.
빌드의 관점에서 본다면 차이점이 존재합니다. 먼저 어셈블리 참조는 단순히 참조만 합니다. 하지만 프로젝트 참조를 하면 참조된 프로젝트의 출력 경로로 빌드된 어셈블리가 생성되고 참조됩니다.
이 차이점을 가지고 참조된 어셈블리의 Properties에 대해서도 살펴보겠습니다. 차이점이 보이실 겁니다. 바로 Specific Version 프로퍼티입니다. 이 프로퍼티는 어셈블리 참조에는 존재하고 프로젝트 참조에는 존재하지 않습니다. 그렇다면 빌드의 관점 뿐만이 아니라 프로퍼티에 대해서도 뭔가 차이점이 존재하는 것 아니냐고 생각하실 수 있습니다. 하지만 명백하게(?!) 빌드의 관점을 제외하고는 차이점이 없습니다.
Specific Version 프로퍼티는 그 이름대로 GAC(Global Assembly Cache)에 등록되어 있는 어셈블리 중에 특정 버전를 참조할 것인지의 여부를 설정하는 것입니다. 프로젝트 참조에서는 위에서 말했듯이 빌드가 된 어셈블리를 참조하기 때문에 특정 버전의 여부가 의미가 없어지는 것입니다. 참고적으로 어셈블리 참조시에 Local Copy는 GAC에 등록여부에 따라서 해당 값이 결정되기 때문에 닷넷 프레임워크에서 제공해주는 System 등등은 false가 됩니다. 프로젝트 참조에서는 Local Copy는 Default로 True 값을 가집니다.
지금까지 차이점에 대해서 이야기를 해보았습니다. 어셈블리 참조를 프로젝트 참조와 동일하게 만들어보도록 하겠습니다.
참조를 하는 프로젝트에서 오른쪽 버튼을 눌러 아래와 같이 Project Dependencies를 선택합니다.
=> Depends on 메뉴에서 어셈블리 참조를 한 ClassLibrary1에 체크 표시를 하고 'OK'버튼을 클릭합니다. Build Order탭을 살펴보면 해당 빌드 순서도 확인하실 수 있습니다. 이렇게 설정을 하고 참조를 하고 있는 프로젝트에서 '빌드'를 실행하면 어셈블리 참조를 한 어셈블리도 빌드가 되는 것을 확인하실 수 있습니다.(물론 어셈블리 참조를 하였더라도 해당 어셈블리로 출력되는 프로젝트는 솔루션에 로드되어 있어야 합니다.) 이러한 정보를 어디서 가지고 있을까요? 바로 솔루션 파일(.sln)에 있습니다.
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsFormsApplication5", "WindowsFormsApplication5\WindowsFormsApplication5.csproj", "{DA5C04D8-F654-49FF-BBEB-CD6BE17AD952}"

           ProjectSection(ProjectDependencies) = postProject

                     {9526A004-DF09-4C1E-9BB2-A1B31B37A716} = {9526A004-DF09-4C1E-9BB2-A1B31B37A716}

           EndProjectSection

EndProject

=> 노란색으로 표시한 부분 아래에 나와있는 Guid가 바로 ClassLibrary1의 Guid입니다. 프로젝트 참조시에는 솔루션 파일에 이러한 정보는 보이지 않습니다. 하지만 Project Dependencies 속성을 확인해보면 자동으로 체크표시가 되어 있는 것을 확인할 수 있는데 해당 정보는 프로젝트 파일(.csproj)에서 얻을 수 있습니다. 어쨌든 Visual Stuido에서는 솔루션 파일과 프로젝트 파일에 있는 정보를 사용하여 빌드 순서를 정해주게 됩니다.
마지막으로 빌드시에 참조되는 어셈블리를 어떠한 경로를 통해서 찾는지 알아보겠습니다.
(여기서는 VS2010기준입니다.)
단순히 나열을 해보도록 하겠습니다.
  {HintPathFromItem}
  {TargetFrameworkDirectory}
  {Registry:Software\Microsoft\.NETFramework,v4.0,AssemblyFoldersEx}
  {AssemblyFolders}
  {GAC}
  {RawFileName}
  bin\Debug\
위의 항목 중에는 단순히 하나의 경로도 있지만 컴퓨터 환경에 따라서 수십개의 경로를 탐색하기도 합니다. 또한 .exe .dll를 모두 찾습니다. 만약에 위의 항목이 실질적으로 경로를 탐색하는 순서라면 bin\Debug\가 제일 마지막에 있는 것이 의외(?)네요. 이러한 정보는 모두 MsBuild를 통해서 얻을 수 있는데 VS에서도 MsBuild에서와 마찬가지로 위의 경로를 탐색합니다.

<2010-06-23>
결정적인 차이점이 존재하네요. 바로 Go To Defenition(정의로 이동?)입니다. 어셈블리 참조를 하면 해당 코드로 이동할 수가 없지만 프로젝트 참조를 하면 해당 코드로 이동이 가능합니다. +_+;;
Posted by resisa
,