System.CommandLine.DragonFruit 를 활용해 CLI 유틸리티를 제작하고 배포를 하려니
닷넷설치가 필요없도록 자체포함을 거니 역시 베이스 용량이 100메가전후로 논다.
CLI유틸리티가 가볍게 쓸라고 만드는건데 어지간한 GUI유틸, 고전게임 쌈싸먹을정도의 용량을 자랑한다.
내장도구를 활용한 높은 개발생산성과 플랫폼범용성, 실행독립성을 모두 잡고 용량과 연산효율을 희생한것이긴 하지만
유틸을 이리저리 아무데서나 자유롭게 옮겨놓고 쓰기엔 불편함을 느낄만한 용량이다.
또한 고작 유틸리티 하나 만들어 쓰자고 사용자에게 최신닷넷 설치를 요구한다거나, 최신도구들로 깔끔하게 만들어놓은 기능들을 닷넷프레임워크용으로 역마이그레이션해 열악한 형태로 다시개발을 하는것도 웃기는일이다. 뭐 필요하다면 기어이 해야하겠지만..
보통의 경우 Trimming 옵션을 줘서 용량을 크게 줄일수 있다.
20메가 정도 용량도 딱히 가볍진 않지만, 요즘같이 인터넷빠르고 용량남아도는 시대에 exe하나에 의존성 전부 쑤셔박아넣은거까지 감안하면 괜찮은 수준이다.
근데 문제는 저 기능이 안쓰는 코드뿐만 아니라 각종 사람이 보기좋게하기위한 메타데이터들도 날려먹다보니 Reflection, 스크립팅 등 다이나믹한 처리에 치명적이고, 안그래도 런타임에서 일일히 디버깅하기 힘든 동적코드인데, 더더욱 예측하고 핸들링하기에 졸라 까다롭다는점이 있어 지금껏 쓸 엄두를 못내고 있었다.
그래서 미루다가 당장 문제가 생긴게, DragonFruit의 CLI파라미터->메인함수파라미터 로 바인딩 하는 방식이 바로 동적으로 메타데이터를 파싱하는 방식이기때문에,
동적으로 Main함수를 찾아내고, 동적으로 파라미터와 주석을 해석해 커맨드명령에 대응시키는 전과정에 장애가 발생하게 되고만것이다.
일단 동적로드할 Main함수 자체부터 컴파일러가 아무도 호출안하는거로 알고 Trim되어 에러가 나고,
용과라이브러리 참조에서 소스생성결과를 build에 Exclude시키고, Main함수를 탐색없이 직접호출시키도록 바꿔보니
해석에 필요한 파라미터 이름 등 해석에필요한 데이터까지 Trimming된 상태인건지 에러가 터졌다.
Trim은 하되 리플렉션하는 요소, 적어도 Main함수만은 존재자체와 메타데이터를 온전히 유지하도록 설정해야했는데,
ChatGPT나 msdn이나 QnA를 구글링해서나 아리까리한 정보밖에 제공받지 못했다.
쨋든 방법을 찾긴 찾았는데, 여전히 표현 의도나 처리방식이 어떻게되는건지 잘 모르겠다.
csproj에 <TrimmerRootDescriptor>로 include할 xml파일을 첨부한다.
xml에는 <linker>하위로 Trim을 무시할 접근정보를 넣고, 해당 접근위치에도
System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute를 추가해야한다.DynamicallyAccessedMemberTypes는 알맞게 맞추거나, 그냥 All로하면 반영되었다. (--help에 쓸 주석까지 포함하려면 All)
<linker>정보를 어셈블리~까지만 입력해도 인식하였지만, type, method를 구체적으로 넣을수록 KB단위로 용량이 더 Trim되었다.
(추가) 참조라이브러리의 동적인부분도 dll명, 네임스페이스+class명 등을 경로로 넣어주면 반영된다.
아래는 삽질만 하고 개같이 실패한 키워드들이다.
DynamicDependency는 권장하지 않는 최후의 수단이라는데, 처음부터 최후밖에 작동하지 않는다.
디씨에서 단어필터링에 막혀서 사진으로 대체하였다
https://learn.microsoft.com/dotnet/core/deploying/trimming/trimming-options
문서를 봐도 제거를 한다는게 Trim을 한다는건지 Trim에서 제거하니까 Trim을 안하도록 뺀다는건지 Root은 뭐고 Link는 무슨의미인지 조또모르겟다.
현 닷넷의 발전방향이 적은 구문만으로 알아서 유연하게 동작하지만, 런타임에서의 유연한(통제하기힘든) 처리들을 Generic, SourceGenerator도구참조 등을 통해 최대한 정적으로 사전작성하여, 용량을 좀 희생하는대신, T4같은 텍스트생성기보다 사용이 일관성 있으면서도 컴파일단에서부터 최대한 처리를 단순&효율적으로 로직을 불변&예측&신뢰성있도록 생산하는것을 지향하고있다.
사전작성된 코드들은 당연히 Trimming등의 컴파일 최적화 동작에도 문제없을것이기 때문에 구식의 동적처리솔루션들이 용량좀 더 줄이자는데 필요한 편의를 지원하는데 굳이 정성들이지 않는것으로 보인다.
그래서, System.CommandLine은 언제까지 구식 시험판으로 냅둘건데..
익명타입이 트리밍되지 않도록 함수의 제네릭 파라미터에다가 <[DynamicallyAccessedMembers] T>다니까 그건 먹음
https://migeel.sk/blog/2024/01/02/building-a-self-contained-game-in-csharp-under-2-kilobytes/