MSBuild를 이용한 닷넷 응용프로그램의 플랫폼(x86/x64)별 빌드
MSBuild를 이용한 배치 파일 팀 빌드 구성을 설명해 드렸었죠.
배치 파일로 팀 빌드 구성
; https://www.sysnet.pe.kr/2/0/905
그런데, x86/x64가 공존하고 있는 요즘에는 위의 빌드 스크립트로는 다소 모자란 감이 있습니다. 즉, 플랫폼별로 별도의 빌드 결과물이 나와줘야 하는 경우가 빈번하기 때문인데요. 가령 \bin 폴더에 다음과 같이 x86/x64용으로 별도의 파일명으로 구성하고 싶을 때가 있습니다.
x86: MyTest32.exe
x64: MyTest64.exe
물론, "AnyCPU" 빌드가 있긴 하지만 COM 개체 참조나 Wow6432Node 레지스트리를 자연스럽게 접근하는 등의 목적에서는 플랫폼을 명시적으로 지정해야만 할 때가 있지요.
위와 같은 가정으로 실제 빌드 스크립트를 구성해 볼까요?
우선, "MyTest" EXE 프로젝트를 만들고 다음과 같이 빌드 스크립트를 만드는 것으로 시작을 해보겠습니다.
msbuild MyTest.csproj /property:AssemblyName=MyTest32;PlatformTarget=x86;Platform=x86
msbuild MyTest.csproj /property:AssemblyName=MyTest64;PlatformTarget=x64;Platform=x64
이렇게 빌드하면 각각 다음의 폴더에 결과물이 출력됩니다.
.\bin\Debug\MyTest32.exe
MyTest32.pdb
.\bin\x64\Debug\MyTest64.exe
MyTest64.pdb
위와 같이 x86과 x64에 대한 모듈을 폴더 단위로 구분하는 것도 좋은 방법일 수 있습니다. 사실 이럴 거면 AssemblyName을 변경할 필요도 없지요. 하지만 저 같은 경우에는 단일 폴더에 있는 것을 선호합니다. 그래서 출력 폴더를 하나로 모으고 싶은데, 이에 대해서는 예전에 설명해 드린 $(SolutionDir) 속성을 통해 제어해 주면 됩니다.
Output 경로에 매크로 상수 사용하는 방법
; https://www.sysnet.pe.kr/2/0/688
그래서 csproj 파일을 수정하고,
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>$(SolutionDir)bin\$(Configuration)\</OutputPath>
...[생략]...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>$(SolutionDir)bin\$(Configuration)\</OutputPath>
...[생략]...
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<OutputPath>$(SolutionDir)bin\$(Configuration)\</OutputPath>
...[생략]...
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>$(SolutionDir)bin\$(Configuration)\</OutputPath>
...[생략]...
</PropertyGroup>
명령행 빌드에서 다음과 같이 수정해 주면 됩니다.
FOR /F %%I IN ("%0") DO SET CURRENTDIR=%%~dpI
SET SOLUTIONDIR=%CURRENTDIR%
msbuild MyTest.csproj /property:AssemblyName=MyTest32;PlatformTarget=x86;Platform=x86;SolutionDir=%SOLUTIONDIR%
msbuild MyTest.csproj /property:AssemblyName=MyTest64;PlatformTarget=x64;Platform=x64;SolutionDir=%SOLUTIONDIR%
그럼, 우리가 원했던 대로 ^^ 아래와 같이 깨끗하게 빌드가 됩니다.
.\bin\Debug\MyTest32.exe
MyTest32.pdb
MyTest64.exe
MyTest64.pdb
그런데, 문제가 하나 있습니다. 바로 "AssemblyName" 속성이 MSBuild 과정에서 전역적으로 적용된다는 것인데요. 쉬운 예를 하나 들자면, 기존 MyTest.csproj가 TestLib.csproj라는 프로젝트를 참조하는 경우에 빌드를 해보면 단번에 알 수 있습니다. 출력 결과물은 예상과는 달리 다음과 같이 됩니다.
.\bin\Debug\MyTest32.dll <== TestLib.dll 의 이름이 변경됨
MyTest32.exe
MyTest32.pdb
MyTest64.dll <== TestLib.dll 의 이름이 변경됨
MyTest64.exe
MyTest64.pdb
참조 프로젝트가 하나라면 그나마라도 정상동작은 하겠지만, 만약 참조하는 라이브러리 프로젝트가 2개라면 2개 모두 MyTest32.dll(MyTest64.dll)로 덮어써져서 DLL 하나가 없어져버려 빌드 자체가 실패해버립니다.
이 문제에 대해서는 다음의 질문/답변에서 우회 방법을 찾아볼 수 있습니다.
Changing the AssemblyName property causes referenced projects output assemblies to have the same exact name?
; http://social.msdn.microsoft.com/forums/en-US/msbuild/thread/54e1cff8-d46a-4b3e-b45b-539ae6512db5/
그래서, 이번에도 어쩔 수 없이 "
Output 경로에 매크로 상수 사용하는 방법"에서 설명했던 것과 유사하게 csproj 프로젝트 파일을 직접 수정을 해줘야 합니다.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="...">
<PropertyGroup>
...[생략]...
<RootNamespace>WindowsFormsApplication1</RootNamespace>
<AssemblyName>WindowsFormsApplication1</AssemblyName>
<AssemblyName Condition=" '$(MyAssemblyName)' != '' ">$(MyAssemblyName)</AssemblyName>
최종적으로 빌드 스크립트는 다음과 같이 구성되어야 합니다.
FOR /F %%I IN ("%0") DO SET CURRENTDIR=%%~dpI
SET SOLUTIONDIR=%CURRENTDIR%
msbuild MyTest.csproj /property:MyAssemblyName=MyTest32;PlatformTarget=x86;Platform=x86;SolutionDir=%SOLUTIONDIR%
msbuild MyTest.csproj /property:MyAssemblyName=MyTest64;PlatformTarget=x64;Platform=x64;SolutionDir=%SOLUTIONDIR%
이제야 우리가 원했던 대로, 빌드 결과물이 출력됩니다. ^^
.\bin\Debug\TestLib.dll
TestLib.pdb
MyTest32.exe
MyTest32.pdb
MyTest64.exe
MyTest64.pdb
첨부한 소스코드는 위의 구성이 적용된 예제입니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]