닷넷 3.5 컴파일러 가져오기

  • Post author:
  • Post category:
  • Post comments:0 Comments
  • Post last modified:February 7, 2020

스크립트 엔진의 단위 테스트 코드를 손보다가 버그를 발견했다. 말이 스크립트 엔진이지 스크립트 언어를 따로 구현해 쓰지 않고 닷넷 프레임워크를 지원하는 C#, VB.NET 등의 언어를 그냥 쓰는데 아무래도 서버 개발자가 스크립트를 직접 개발하는 초기 단계다 보니 C#이 주 언어가 됐다. 그런 탓에 단위 테스트 코드도 C#으로 작성했는데 이런 식이었다.

using (StreamWriter sw = new StreamWriter(File.Open(path, FileMode.Append)))
{
	sw.WriteLine(ByeCount);
}

이 코드를 간결하게 한답시고 var 키워드를 적용했다.

using (var sw = new StreamWriter(File.Open(path, FileMode.Append)))
{
	sw.WriteLine(ByeCount);
}

여기까진 좋았다. 닷넷 프레임워크 3.5를 도입했으니 최신 기능을 써서 나쁠 건 없다. 그러나 문제는 스크립트 엔진이 이 코드를 런타임에 컴파일하다 발생했다. var 키워드를 인식 못하는지 컴파일러 예외가 튀어나왔다. 이상하다 싶어 단위 테스트에 중단점을 걸고 살펴봤다. 원인은 컴파일러 버전이 3.5가 아닌 2.0이라는 점이었다.

// language는 "C#", "Visual Basic .NET" 같은 문자열이다.
CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider(language);

원래는 위의 코드처럼 필요한 컴파일러 인스턴스를 불러왔다. 그러나 이 방식으론 var 키워드 등을 지원하는 C# 컴파일러 3.5가 나오지 않는다. 이 코드를 담은 어셈블리가 닷넷 프레임워크 3.5를 이용해 작성됐더라도 말이다. 컴파일러 버전 3.5가 필요하다면 이를 명시해야 한다. 그 방법은 크게 두 가지인데 Compiling with CodeDomProvider doesn’t allow new features of C# or VB란 글에 잘 정리되어 있다.

방법 1 – app.config에 명시하기

예제만 보면 한눈에 무슨 이야기인지 안다. 더 이상의 설명은 불필요하다.

<system.codedom>
  <compilers>
    <compiler
      language="vb;vbs;visualbasic;vbscript"
      extension=".vb"
      type="Microsoft.VisualBasic.VBCodeProvider, System,
        Version=2.0.3600.0, Culture=neutral,
        PublicKeyToken=b77a5c561934e089"
      compilerOptions="/optimize"
      warningLevel="1" >
      <providerOption
        name="CompilerVersion"
        value="v3.5" />
    </compiler>
    <compiler
      language="c#;cs;csharp"
      extension=".cs"
      type="Microsoft.CSharp.CSharpCodeProvider, System,
        Version=2.0.3600.0, Culture=neutral,
        PublicKeyToken=b77a5c561934e089"
      compilerOptions="/optimize"
      warningLevel="1" >
      <providerOption
        name="CompilerVersion"
        value="v3.5" />
    </compiler>
  </compilers>
</system.codedom>

방법 2 – 컴파일러와 그 버전을 명시하기

이 방법도 아주 간결하다.

var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });

방법 3 – 좀더 세련된 방법

잘 들어

좀더 세련된 방법이란 방법 2를 개선한 것이다. CodeDomProvider.CreateProvider(language)를 사용했던 처음 코드는 간결했다. VB.NET을 쓸지 C#을 쓸지 명시해주면 알아서 필요한 CodeDomProvider 인스턴스를 반환한다. 그러나 불행히 CreateProvider 메서드는 CompilerVersion 값을 받는 매개변수가 없다. 그러니 if문을 작성해 language 값에 따라 CSharpCodeProvider 인스턴스를 생성하던가 해야 한다. 그러나 이렇게 지저분한 코드는 짜증만 돋군다. 여기 조금 더 나은 방법이 있다.

CodeDomProvider codeDomProvider = GetCodeDomProvider(language);

private static CodeDomProvider GetCodeDomProvider(string language)
{
	var providerOptions = new Dictionary<string, string>
							  {
								  {"CompilerVersion", "v3.5"}
							  };

	Type codeDomProviderType = CodeDomProvider.GetCompilerInfo(language).CodeDomProviderType;
	ConstructorInfo constructor = codeDomProviderType.GetConstructor(
					new Type[] { typeof(IDictionary<string, string>) }
					);
	return (CodeDomProvider)constructor.Invoke(new object[] { providerOptions });
}

참고로 이 코드는 CodeDomProvider.CreateProvider(language)의 구현을 Reflector로 분석한 다음 살짝 고친 것이다.

Author Details
Kubernetes, DevSecOps, AWS, 클라우드 보안, 클라우드 비용관리, SaaS 의 활용과 내재화 등 소프트웨어 개발 전반에 도움이 필요하다면 도움을 요청하세요. 지인이라면 가볍게 도와드리겠습니다. 전문적인 도움이 필요하다면 저의 현업에 방해가 되지 않는 선에서 협의가능합니다.
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments