뒤로가기back

윌슨의 개발지식- 개발환경과 빌드(2)

2019.10.22 by Gaudio Lab

개발환경과 빌드(2)

개발환경과 빌드는 도메인마다 다 다르고, 복잡해 보이기 때문에 접근하려고 마음 먹기 전에 두려움이 먼저 자리 잡는 것 같아요. 이러한 새로운 개발환경을 처음 접할 때 가장 어려운 부분은 전체 시스템에 대한 이해가 완벽하지 않기 때문에, 한 수 한 수 둘 때마다 이것이 어떠한 사이드 이펙트를 만들지에 대해서 불안감과 두려움이 먼저 생긴다는 것이죠. 전체 시스템에 대해서 확신을 가지고 있는 누군가가 옆에서 불확실하다고 여겨질 때마다, 확신을 준다면, 배움의 속도도 빨라지고, 응용의 속도도 빨라질 것 같다는 생각이 들었습니다.

그래서 도메인 별로 개발환경에 대해서 한 번 정리하고 많은 분들이 불안한 마음으로 다루고 있는 빌드 시스템에 대해서 알리고자 이 문서를 기획했습니다.

 

Web Javascript Library 빌드와 개발환경

빌드

웹도 네이티브 빌드와 크게 다를 게 없습니다. 결과적으로 해당 플랫폼 런타임에서 사용할 수 있는 라이브러리 또는 실행 스크립트를 배포하는 것이죠. 다만 그 타겟 플랫폼이 Chrome, Safari, IE 등의 브라우저고 기계가 이해할 수 있는 언어로 번역하는 시점이 좀 다릅니다. 브라우저는 Java의 Virtual Machine처럼 텍스트 기반의 Javascript 파일을 Runtime에 기계어로 번역하는 JIT 자바스크립트 엔진을 가지고 있어서 클라이언트가 브라우저에서 웹페이지를 접근하는 시점에 스크립트를 번역합니다.

 

그러면 특정 플랫폼에서 사용가능한 자바스크립트 라이브러리 파일을 배포하기 위해서는 무엇을 고려해야 할까요?

 

브라우저 타겟 고려사항

  1. OS : 똑같은 버젼의 브라우저일지라도 모바일과 데스크탑 브라우저에서 지원하는 API 지원여부가 다를 수 있어요. 한 예로 Safari 브라우저는 모바일에서는 video.volume이 readonly인 반면 데스크탑에서는 값 할당이 가능하죠.

  1. 브라우저 종류 : Chrome, Edge, IE, Safari, Opera 등 브라우저들마다 표준 Web API를 구현한 수준이 다릅니다. 대표적인 예로 MSE를 들 수 있어요. IE에서는 이 API를 지원하지 않고 있다가 최근에서야 Netflix와 공동으로 이 API를 구현했죠.
  1. 브라우저 버젼 : 브라우져 버젼별로 지원하는 Web API가 다를 수가 있어요. 한 예로 스트리밍 플레이어 개발에 필수적인 API인 MSE는 크롬 55 버젼부터 사용이 가능합니다. (참고로 크롬은 버젼 77까지 출시되었다.)
  1. Web API & Polyfill : 브라우저마다 표준 API 지원여부가 다릅니다. 지원하지 않을 경우 Polyfill(브라우저에서 지원하지 않지만 워크어라운드로 구현한 라이브러리 또는 SDK)을 서치해서 찾아야 하죠.
  1. Javascript 버젼 : 최신 브라우저 버젼 대부분 ES 최신 문법을 지원하는 반면 오래된 버젼이나 구형 브라우저에서는 이를 지원하지 않을 수 있습니다. 더 넓은 범위의 호환성을 지원하려면 타겟 OS/브라우저를 설정하고 이에 따라 문법 지원여부를 고민해야 합니다.

 

위 고려사항을 생각하고 범용성을 고려하면 개발 프로세스는 다음처럼 생각할 수 있어요.

 

개발 프로세스

대개 개발 워크플로우는 다음과 같이 크게 3가지로 볼 수 있을 것 같아요.

  • 개발
  • 테스트
  • 배포

위와 같은 3가지 축으로 볼 때 개발 워크플로우를 다음과 같이 표현할 수 있어요.

 

개발 환경

일반적으로 C/C++ 네이티브, 안드로이드, 맥/iOS 개발에 익숙한 분이라면, 웹 개발을 시작할 때 당황할 수 있습니다. XCode, Android Studio, Visual Studio 등 IDE에서 제공하는 기본적인 기능조차 설정되어 있지 않죠. 예로 들어, XCode에서 C++개발을 하면, 파일 단위로 기능을 나눠서 구현하고 그 헤더를 추가함으로써 다른 파일에 정의되어 있는 기능들을 사용합니다. 이렇게 파일을 분리해서 개발하면 나중에 필요한 파일만 재사용함으로써 개발 효율성을 높일 수 있겠죠. 또한 변경된 파일만 재빌드해서 빌드속도도 향상됩니다.

 

그러나 웹 개발 프로젝트를 시작할 때는 간단한 기능도 프로젝트를 시작하는 개발자가 세팅하고 가야 합니다. 이러한 개발 도구들을 많은 분들이 알고 계시는 NodeJS를 통해서 효과적으로 도입하고 사용합니다.

 

Node JS가 효과적인 이유는 여러가지가 있는데, 첫 번째로, 백엔드와 프론트 엔지니어가 공통의 언어로 작업공간을 공유할 수 있기 때문이고 두 번째로는 NPM 패키지 매니저가 의존성 관리를 너무나 편안하게 만들어 주기 때문입니다. (npm이나 기본적인 Node JS 개념을 이해하지 못하고 있다면, 여기서 좀 어려워질 수 있습니다.)

 

그렇게 해서 Node JS 기반 위에 개발자들은 하나둘씩 개발도구 패키지들을 배포하기 시작했고, 스트리밍 빌드시스템인 gulp, node module을 클라이언트에서도 사용하게 해주는 browserlify, 테스크 러너인 gulp 등등이 그것입니다. 문제는 어느 개발환경 하나 똑같은 개발의존성으로 구성하는 경우가 드물기 때문에 이 많은 패키지들의 개념들을 이해하고 감당하기가 어려웠던 것 같습니다. 자바스크립트 모듈 번들러인 Webpack이 나오면서 이 모든 기능들을 하나의 패키지에서 사용할 수 있게 되자, 대부분의 개발자들은 Webpack 기반으로 개발환경을 구성하는 추세로 돌아선 것 같습니다.

 

Webpack

웹팩은 Javascript Module Bundler입니다. 웹팩이 하는 일은 네이티브 C++빌드 환경에서 링커가 하는 역할과 매우 유사합니다. 링커가 의존성 있는 오브젝트 파일들을 모아 하나의 다이나믹 라이브러리 파일로 병합하는 것처럼, 웹팩 또한 여러 의존성 있는 JS파일들을 하나의 파일로 묶어서 출력합니다. (물론, 웹팩은 단순히 JS파일 뿐만 아니라, CSS파일, 이미지 파일들도 로드하지만, 결과적으로는 JS로 변환하기 때문에 이는 아직은 생각하지 않으셔도 됩니다)

 

웹팩은 이해하는 데 필수적인 개념은 Webpack 공식 홈페이지 문서에 잘 설명되어 있습니다. 바로 Entry, Loader, Plugin입니다.

 

Entry는 C++의 export API의 개념과 유사합니다. 웹팩이 가장 처음 읽는 파일로, 이 파일을 읽어서 의존성 트리를 그립니다. 이 엔트리를 몇 개 설정하는지에 따라 최종 JS파일 개수가 결정됩니다.

 

Loader는 각 파일을 병합하기 전에 특정 파일에 처리할 컨버터라고 보시면 됩니다. 예로 들어, Babel Loader는 일반적으로 좀더 상위 버젼의 자바스크립트로 작성된 JS파일을 좀 더 많은 브라우저가 이해할 수 있는 언어로 변경하는 역할을 합니다.

 

Plugin은 최종적으로 병합된 JS파일을 후처리하는 모듈입니다. 예로 들어, Uglify Plugin은 JS파일의 최종 파일 사이즈를 줄이고 난독화를 하기 위해, 변수명을 이해하기 힘든 명칭으로 변경하고 변수 길이나 필요없는 스페이스를 없앱니다.

 

(Entry 파일이 무엇이고, 사용하는 Loader, Plugin을 설정하는 것은 .config.js 파일에 정의합니다.)

 

이처럼 다양한 기능을 제공하는 Webpack을 웹 라이브러리 개발 프로세스에서 매치시켜 보면 다음과 같습니다.

 

 

사용된 로더와 플러그인은 아래 간단히 설명합니다.

 

Mocha

유닛테스트 프레임워크입니다.

 

Chai

노드와 브라우저용 BDD/TDD Assertion 라이브러리로 Mocha 유닛 테스트 프레임워크와 연동해서 주로 사용합니다.

 

Karma

테스트 러너로, 현재 호스트 시스템에 설치된 브라우저를 디텍트하고 설정된 브라우저를 실행시켜서 그 위에서 설정된 테스팅 프레임워크로 작성된 테스트 코드를 실행합니다.

 

ESLint

코드 정적 분석과 코딩 스타일 반영을 위해서 사용되는 툴입니다.

 

Babel

트랜스파일러로, 다양한 브라우저에 범용적으로 사용될 수 있는 자바스크립트 버젼으로 언어를 번역하는 역할을 수행합니다.

 

Webpack Banner Plugin

스크립트 파일에 상단에 커멘트로 스크립트 파일에 대한 정보를 일괄적으로 담기 위해서 사용됩니다. 예로 들어, 회사명이나 작성자 정보 등이 있습니다.

 

JSDoc

자바스크립트의 문서화 툴. 자바스크립트 파일에 문서화 문법에 맞춰 작성된 커멘트를 파싱해서 문서를 출력합니다.

 

레퍼런스

Webpack Official Website https://webpack.js.org/concepts/

pre-image
윌슨의 개발지식- 개발환경과 빌드(1)

개발환경과 빌드(1) 개발환경과 빌드는 도메인마다 다 다르고, 복잡해 보이기 때문에 접근하려고 마음 먹기 전에 두려움이 먼저 자리 잡는 것 같아요. 이러한 새로운 개발환경을 처음 접할 때 가장 어려운 부분은 전체 시스템에 대한 이해가 완벽하지 않기 때문에, 한 수 한 수 둘 때마다 이것이 어떠한 사이드 이펙트를 만들지에 대해서 불안감과 두려움이 먼저 생긴다는 것이죠. 전체 시스템에 대해서 확신을 가지고 있는 누군가가 옆에서 불확실하다고 여겨질 때마다, 확신을 준다면, 배움의 속도도 빨라지고, 응용의 속도도 빨라질 것 같다는 생각이 들었습니다.   그래서 도메인 별로 개발환경에 대해서 한 번 정리하고 많은 분들이 불안한 마음으로 다루고 있는 빌드 시스템에 대해서 알리고자 이 문서를 기획했습니다.   Native C,C++,WebAssembly 빌드 바라건대, 이 문서를 읽고 새로운 타겟 플랫폼에 대한 빌드 요청이 들어 왔을 때, 기반이 되는 개념모델을 활용하여 유연하게 문제를 해결해 나갈 수 있었으면 합니다. 🙂   컴파일이란? 작성된 소스 파일들을 특정 플랫폼(런타임)에서 이해할 수 있는 언어(일반적으로 기계어에 가까운 언어를 지칭)로 변환하는 프로세스에요.   플랫폼이란? 플랫폼이라는 용어는 일반적으로 “아키텍쳐“와 그 위에 올라오는 “운영체제” 쌍을 의미합니다. 그러나 최근에는 좀 더 의미가 확장되어 운영체제 위에 “소프트웨어“가 추가된 형태로 얘기되는 것 같아요. 저는 좀 더 일반적으로 생각하기 위해서 “소프트웨어 또는 라이브러리가 실행될 수 있는 환경을 위한 조건“이라는 개념을 가지고 있되, 컨텍스트에 따라서 해석하고는 합니다. 예로 들어, C++환경에서는, “아키텍쳐-OS”로 생각하는 것이죠.   빌드란? 특정 플랫폼의 빌드머신에서, 타겟 플랫폼의 런타임 위에서 동작하는 프로그램이나 사용할 수 있는 플러그인, 프레임워크, 라이브러리, SDK 등을 제작하는 프로세스. 일반적으로 컴파일 과정을 포함해요.   그러면 크로스플랫폼 빌드란? 특정 플랫폼의 빌드머신에서,하나 이상의 다양한 타겟 플랫폼의 런타임 위에서 동작하는 프로그램이나 플러그인, 프레임워크, 라이브러리, SDK 등을 생산하는 프로세스.   런타임 이렇게 프로그램이 실행될 수 있는 환경을 실행환경(이것도 결국 프로그램), 즉 Runtime Environment라고 합니다. 예로, Java는 해당 OS에 설치된 JRE(=Java Runtime Environment)라는 프로그램 위에서 자바의 IR 코드가 컴파일되어 실행되는 것이죠.     그러면 C++ 컴파일이란? 컴파일 프로세스 오브젝트 파일 컴파일   정적 라이브러리나 실행파일을 빌드한다고 해도 기본적으로 오브젝트 파일부터 컴파일을 마치고 그 결과물이 입력이 되어 정적 라이브러리나 실행파일 컴파일이 진행됩니다. 따라서 가장 기본이 되는 것은 C/C++ 컴파일 과정이라고 볼 수 있는데, 크게 다음의 과정으로 진행됩니다.   전처리 이 과정은 전처리매크로(Pre-processor Macro)를 읽고 처리하는 과정입니다. 가장 많이 알고 있고 많이 쓰는 전처리 매크로는 다음과 같아요. #include 컴파일러가 위 전처리 매크로를 만나면 hello.h 파일을 읽어서 #include한 .cpp파일에 복붙합니다.   컴파일 전처리 과정을 거친 소스파일(.i)들은 컴파일 과정을 거치면서 어셈블리로 출력됩니다. 이 과정을 수행하는 툴채인 프로그램이 compiler입니다. 이 단계가 마지막 Human Readable한 코드에요. 그리고 이 단계에서 Architecture 인스트럭션으로 코드가 생산됩니다. 즉, 어셈블리 코드는 타겟 아키텍쳐에 dependant합니다. *참고로 Assembly라는 용어는 일반적으로 기계어에 가장 가까운 Human Readable한 코드를 말한다고 하네요.   어셈블 컴파일을 거쳐서 나온 어셈블리 파일들을 어셈블하면 최종적으로 머신코드인 오브젝트 코드 파일(.o)들이 생성됩니다. 이 과정은 assembler가 수행합니다. 오브젝트 코드란? 출처: Compiler Design | Introduction of Object Code   오브젝트 코드는 기계가 이해할 수 있는 코드로 구조는 위 그림과 같아요.   여기서 Header에는 별 정보가 없고 그 아래 Text Segment, Data Segment 등의 위치에 대한 인덱스를 담고 있다네요.   여기서 중요한 정보는 “Symbol Table”과 “Debugging Information” 정도일 것 같아요. 이 “Symbol Table”을 이용해서 Linking이 일어나고, “Debugging Information”을 이용해서 디버깅이 이루어집니다. (참고로 Debugging Information은 컴파일러에 -g 옵션(GCC 기준)을 주어야 포함됩니다.)   Symbol에 대한 정보는 Mac에서는 otool, nm 툴 등을 이용해서 확인할 수 있습니다. 간단히 보면, 아래와 같습니다.   > nm libExampleFx.a example_filter.cpp.o: U __ZN12Example13processEP9AudioNodePKsPs -------- T _example_context_create 위를 해석하면, libExampleFX.a라는 정적 라이브러리는 example_filter.cpp의 오브젝트 코드를 포함하고 있다. 그 오브젝트 코드에는 두 개 함수가 선언되어 있다. Example::Process… example_context_create “U”라고 앞에 표시된 Example::Process…은 이 오브젝트 코드의 Undefined된 함수다. “T”라고 앞에 표시된 example_context_create는 이 오브젝트의 정의된 함수다. 이 밖에도 많은 심볼이 있지만, 지금은 “U”라는 키워드가 정의되지 않은 심볼이라 링킹 과정을 통해 심볼이 정의되어야resolved 한다는 점만 기억하시면 되요.   정적 라이브러리(static library) 빌드 정적 라이브러리 빌드는 오브젝트 파일 컴파일 프로세스 뒷단에 아카이브 과정이 포함됩니다. 그리고 이 아카이브를 수행하는 툴채인 프로그램이 archiver에요. 아카이브는 오브젝트 파일을 zip한다는 개념으로 보시면 되요. 소스파일의 개수만큼 출력되는 오브젝트 파일을 하나의 파일로 합쳐서, 좀 더 포터블한 형태로 만드는 과정이죠. 실행파일(executable) & 동적 라이브러리(shared library, dynamic library) 컴파일 동적 라이브러리 또는 실행파일 컴파일 또한 오브젝트 파일 컴파일 프로세스 뒷단에 링킹 과정이 포함된 빌드 과정이에요. 링킹 프로세스에서는 오브젝트 파일을 하나로 병합합니다. 그리고 이 링킹을 수행하는 툴채인 프로그램이 linker입니다. 이 때 각각의 오브젝트 파일에 Undefined되어 있는 심볼(함수, 변수 클래스 등등)을 다른 오브젝트에서 찾아서 교체하는 작업이 일어납니다. 따라서 링킹 과정이 일어나기 전까지는 내가 사용하려는 정적 라이브러리가 제대로 빌드된 것인지를 확인하기가 어렵습니다. 빌드 도중 많이 접하게 되는 “symbol not found” 에러가 이 때 발생(아래 스크린샷 참고)하죠.   출처: Symbol(s) not found for architecture x86_64 when using C/C++ library in Xcode project   툴체인이란? 개발용 프로그램 도구 집합을 툴체인이라고 하는데요. 툴체인이라고 부르는 이유는 한 프로그램 출력이 다른 프로그램의 입력이 되어 서로 체인처럼 연결되어 실행되기 때문이라네요. 컴파일러의 출력은 어셈블러의 입력이 되고 어셈블러의 출력은 아카이버의 입력이 되는 것처럼 말이죠.   컴파일러 툴체인 생산자 컴파일러 툴채인의 생산자는 크게 2개로 나뉘어 집니다. GNU의 GCC LLVM Developer Group의 LLVM(Low Level Virtual Machine) 두 생산자의 차이점을 이해하려면 컴파일러의 구조를 이해해야 해요. 전통적으로 컴파일러의 구조는 아래처럼 “3 Phase Design”을 따르고 있다네요. 참조: http://www.aosabook.org/en/llvm.html   문제는 GCC를 포함해서 많은 컴파일러가 이 구조를 따르고 있음에도 불구하고, Frontend, Optimizer, Backend가 깔끔하게 분리 되기에는 아직 의존성이 너무 얽혀 있다고 하네요. 그런데, LLVM은 중간과정에 LLVM IR(Intermediate Representation)을 도입하면서 재사용이 용이한 컴파일러를 구현해 냅니다. 우선 각각의 파트가 담당하는 역할을 정리해 보면 다음과 같아요. Frontend: 언어해석. C++, C, Rust low한 언어를 해석 Optimization: 최적화 Backend: 머신언어로 변환. 이 때 Machine Dependent한 코드가 생산됩니다. 즉 해당 아키텍쳐만 이해할 수 있는 언어로 번역되는 것이죠.   그렇다면 C++ 언어를 해석하는 Frontend와 x86_64 아키텍쳐용 백엔드를 각각 한 번만 만든 후에 이후에는 이를 독립적인 프로그램으로 빌드해서 운영하면, 타겟 플랫폼과 사용언어에 따라 필요한 Frontend, Backend 툴채인을 선택해서 빌드하면 되는 것입니다. 따라서 기존 컴파일러가 각각의 파트가 서로 의존성이 있기 때문에, 언어 하나와 백엔드 하나당 컴파일러가 한 대씩 있어야 했다면, LLVM은 호스트 플랫폼용으로 한 번만 빌드하면, 이후에는 프론트엔드 언어가 추가되거나, 머신이 추가될 때에만 툴채인을 받아서 배포하면 되는 것이죠. 참고로, 여기서 LLVM의 C/C++ 언어 Frontend로 사용되는 것이 바로 Clang입니다.   스탠다드 라이브러리(STD)란?  “C++ 스탠다드 라이브러리는 C++과 C++ ISO 표준 자체로 쓰여진 클래스들과 함수들의 집합” – 출처: Wikipedia 신기한 점은 ISO에서 헤더만 표준으로 제안할 뿐 구현은 각 벤더(?)가 해야 한다는 점이에요. 따라서 Microsoft는 본인들의 구현 라이브러리를 가지고 있고, GNU도 Linux용 독자적인 구현 라이브러리를 가지고 있는 거죠. 그럴 수 밖에 없는 이유라면 STD의 특정 파트의 구현은 OS의 System API에 의존적일 수 밖에 없기 때문이죠. 예로 들어, 같은 경우가 그렇습니다. 이 헤더의 구현은 OS C API를 사용해서 구현하게 됩니다. 가끔 접하게 되는 gnustl, stdc++ 등의 단어들은 서로 다른 벤더가 구현한 STD 라이브러리를 얘기합니다.   스탠다드 라이브러리 Extension이란? ISO에서 스탠다드 라이브러리의 기능을 확장한 표준안이 발표되면, 이 또한 각 벤더들이 새롭게 구현해야 합니다. 그런데 벤더들마다 구현의 속도가 다르고 어떤 벤더는 구현을 했고, 또 어떤 벤더는 구현을 못한 경우가 있기 때문에, 툴채인마다 확장기능을 제공하는 수준이 다릅니다.   그러면 C++ 크로스플랫폼 빌드란? “특정 플랫폼 빌드머신에서 C++로 작성된 소스를 하나 이상의 다양한 타겟 플랫폼의 런타임 위에 설치하여 동작하는 프로그램이나 플러그인, 프레임워크, 라이브러리, SDK 등을 생산하는 프로세스“라 말할 수 있겠죠. 이에 따르면, 크로스플랫폼 빌드를 위해 다음을 고려해야 합니다. 빌드 머신 또는 호스트 시스템의 플랫폼 타겟 플랫폼 빌드 툴채인 타겟 플랫폼 스탠다드 라이브러리   쉽게 예를 들어 보면, 내가 만약 armeabi-v7a 아키텍쳐, Android OS 위에서 동작하는 C++로 개발된 라이브러리를 빌드하려면, 먼저 어디서 빌드할 것인지를 결정해야 합니다. 만약 Mac OSX로 선정했다면 이 OS에서 타겟 플랫폼인 “armeabi-v7a-android”를 컴파일할 수 있는 빌드 툴채인과 스탠다드 라이브러리를 지원하는지 확인합니다. 지원하면 빌드할 수 있는 거겠죠. 그렇다면, 위에서 살펴본 고려사항에 기반해서 주요 5개 타겟 플랫폼의 빌드환경을 살펴보자.   타겟 플랫폼별 빌드환경     타겟 플랫폼 빌드 FAQ Windows /MD, /MT 옵션이란? 윈도우에서는 Standard Library가 CRT 라이브러리에 의존적인데, MD는 CRT dll라이브러리를 동적으로 링킹해서 사용하는 옵션이고, MT는 CRT lib라이브러리로 링킹해서 사용하겠다는 것이에요.   MD의 경우, CRT 라이브러리가 최종 바이너리에 포함되지 않기 때문에, 파일 사이즈가 작아집니다. 반면에, 시스템에 CRT dll이 없는 경우 동작하지 않겠죠. 반대로 MT는 라이브러리가 최종 바이너리에 포함되기 때문에 시스템과 상관없이 항상 동작합니다. 반면에 파일 사이즈가 크겠죠.   *주의할 점은 MD옵션으로 빌드한 라이브러리를 사용하려면 마찬가지로 MD옵션으로 라이브러리 또는 실행프로그램을 빌드해야 합니다. (MT도 마찬가지)   왜 윈도우는 동적 라이브러리가 .lib와 .dll로 생성되는가? 앞에서 얘기했듯이, 오브젝트 코드는 Symbol Table을 포함하고 있기 때문에, 이를 기반으로 링킹이 진행되죠. 마찬가지로 이 오브젝트 코드가 병합된 형태인 dynamic 라이브러리(또는 shared library) 또한 로드 타임에 이 심볼테이블을 이용해서 링킹이 일어납니다. 이 심볼테이블이 윈도우에서는 .lib파일에 포함된 것입니다. 그리고 .lib파일을 “Import Library”라고 부릅니다. 다른 플랫폼은 윈도우와는 다르게 동적 라이브러리 파일 하나에 심볼 테이블이 포함되어 있다고 합니다. 참고로 Dynamic 라이브러리의 링킹방법은 두 가지가 있고 하나는 load 타임 링킹이고 두 번째는 runtime 링킹입니다. 후자는 심볼 테이블이 필요없이 함수를 다이렉트로 호출하는 것으로 알고 있습니다. Cygwin vs MinGW vs MinGW-w64 윈도우 빌드를 윈도우가 아닌 다른 빌드머신에서 하려고 할 때 가장 혼란을 주는 개념들이 바로 Cygwin, MinGW, MinGW-w64입니다.   MinGW는 “Minimalist GNU for Windows의 약자로 윈도우 환경에서 GNU 툴채인을 이용하기 위한 환경을 의미합니다.” MinGW에는 컴파일러 툴채인 및 플랫폼 헤더와 라이브러리가 포함되어 있습니다. Microsoft에서는 이 플랫폼 라이브러리 소스를 오픈하지 않았기 때문에, MinGW는 플랫폼 라이브러리 헤더인 windows.h를 MSDN에 문서에 기반해서 순수히 구현했다고 하네요. 따라서 불완전하기도 하고 최신 API는 빠져있는 경우가 있다네요. 또한, MSVC로 빌드한 C++라이브러리는 C Runtime Library에 의존적인데 이 또한 오픈되어 있지 않기 때문에 손수 구현했다고 합니다.   MinGW-w64는 MingGW와는 완전히 별개라고 합니다. OneVision이라는 회사에서는 자사 라이브러리를 64bit 윈도우 타겟으로 포팅하기 위해 64비트 윈도우용 툴채인을 직접 제작했다고 합니다. 이렇게 제작된 툴채인을 MinGW에 제출했지만, 코드를 확신할 수 없었던 MinGW에서 코드를 거절했고, 이에 완전히 별개로 MinGW-w64 라는 이름으로 프로젝트를 운영하기 시작했다고 합니다. MinGW가 32비트만 지원하는 반면, MinGW-w64는 64비트 뿐 아니라 32비트도 지원한다고 하네요.   Cygwin은 완전히 타겟이 다른 빌드환경입니다. Cygwin은 윈도우 OS위에 리눅스 에뮬레이팅 레이어를 올리고 그 OS에서 동작하는 라이브러리 또는 실행 프로그램을 빌드합니다. 결국, Cygwin으로 빌드하면 Cygwin이 타겟인 것입니다. 프로그램을 받은 유저 또한 Cygwin이 설치되어 있어야 해당 바이너리를 실행할 수 있습니다.   Android NDK란? ndk-bundle은 많은 툴채인들이 포함된 도구집합이다. 이 중 ndk-build 프로그램은 Android.mk와 Application.mk를 입력파일로 받아서 여기에 정의된 설정값에 따라 ndk-bundle에 포함되어 있는 clang, gcc, archiver, assembler 등 툴채인들을 선택해서 빌드를 수행한다.   Web Browser와 Web Assembly Emscripten이란? Emscripten은 LLVM을 이용해서 만든 컴파일러로, Frontend는 그대로 사용하되 Optimizer와 Backend를 웹어셈블리용으로 개발한 컴파일러다. 여기서 Emscripten에 포함된 웹어셈블리용 Optimizer를 Binaryen이라고 부른다. 자세한 컴파일 과정은 아래와 같다. (웹어셈블리에 대해서는 다음에 더 자세히 설명하도록 할게요)   CMake란? 위에서 언급한 C++ 컴파일 프로세스를 고려하면, 특정 타겟 플랫폼에 대해서 소스를 컴파일, 패키지하고, 헤더파일 추가나 소스파일 변경을 감지하는 것이 바로 “빌드시스템”이죠. 이 빌드시스템이 바로 make, ninja고 Visual Studio고, XCode에요. 그리고 빌드시스템을 생성하는 것이 바로 “CMake”입니다.   타겟 플랫폼으로 소스를 빌드하는 시스템을 생성하려면 어떤 입력Input이 필요할까요? 용하는가? 어떤 linker를 사용하는가? 컴파일 대상이 되는 소스 파일과 헤더 파일은? 빌드 옵션을 어떻게 줄 것인가? compiler 옵션을 어떻게 줄 것인가? assembler 옵션을 어떻게 줄 것인가? linker 옵션을 어떻게 줄 것인가? 스탠다드 라이브러리는 무엇을 쓸 것인가? 어떤 형태의 라이브러리 또는 실행파일을 빌드할 것인가? 어떤 빌드 시스템을 생성할 것인가? 여기에 크로스 컴파일을 위해서 추가적으로 고려해야 하는 점은 다음과 같아요. 빌드 툴채인마다 다른 컴파일러, 어셈블러, 링커 옵션들 : 왜냐하면, gcc와 clang, MSVC에 따라 컴파일러 옵션의 컨벤션과 키워드가 다릅니다. *따라서 CMake에서 컴파일 옵션을 문자열로 주면 특정 컴파일러에서만 동작할 리스크가 있습니다. 예로 들어, “-std=c++11” *CMake에서는 컴파일에 상관없이 표준화된 인터페이스를 제공하기 위해 가급적으로 CMake에서 제공되는 feature로 이러한 옵션을 정의하기를 권장하는 추세죠. 다만, 아직까지 완벽하지는 않습니다. 다음은 CMake에서 지원하는 feature 옵션 함수에요. target_compile_features(myTarget PUBLIC cxx_variadic_templates cxx_nullptr PRIVATE cxx_lambdas )   위와 같은 입력은 다음처럼 CMake의 커맨드 또는 파일에 매핑될 수 있어요.   이렇게 보면 CMake의 각각의 커맨드가 어떤 역할을 하는지 명확하지 않나요?   CMakeLists.txt에서는, add_library로 내가 정한 이름으로 target 객체를 생성합니다. 그리고 이 추가된 타겟 객체에 target_compile_options, target_compile_features, target_include_directories 명령어를 호출하여 컴파일 옵션, 컴파일 기능, 그리고 헤더파일을 추가하죠.   cmake명령어의 옵션 중 DCMAKE_TOOLCHAIN_FILE에 툴채인 파일을 넘깁니다. 마찬가지로 -G 옵션을 추가해서 빌드시스템 타입을 정의합니다. 다만 Make 빌드시스템은 기본값으로 설정되어 있어서 -G 옵션을 줄 필요가 없습니다.   타겟이란? 계속해서 “타겟”이라는 단어를 언급했는데, CMake와 빌드시스템 등을 이해할 때 가장 핵심이 되는 것은 위에서 얘기한 빌드에 대한 이해도와 타겟의 개념이에요.   타겟은 빌드의 최소 단위에요. 그리고 그 빌드의 최소가 되는 단위를 만드는 CMake command는 아래처럼 딱 3개 뿐입니다.   add_library. 옵션은 OBJECT, STATIC, SHARED add_executable add_custom_target   CMake Process file:///Users/gaudio-el/Downloads/wilson-dev-blog/Untitled/Untitled/Native%20C%20C%20WebAssembly/CMake.html그러면 CMake는 입력들을 받아서 어떤 프로세스를 거칠까요?   위에 다이어그램에 표시되어 있듯이, 크게 2개 프로세스로 구분됩니다. Configure Step Generate Step “Configure Step” 때는 CMakeCache.txt파일이 생성됩니다. 이 CMakeCache.txt파일에는 다음이 정의되어 있습니다. 컴파일러, 아카이버, 링커 경로 C,C++ 컴파일러 옵션 유저가 정의한 변수 그리고 이 파일이 이미 있으면, CMake는 “Build System”을 생성하는 Generate 스텝으로 바로 넘어갑니다.   빌드시스템을 한 번 생성하면 부분 컴파일을 지원하기 때문에, 소스를 수정하고 빌드해도 변경된 소스만 재컴파일합니다.   그러나, CMake로 빌드시스템을 재생성하게 되면 전체 파일을 다시 컴파일해야 하기 때문에 효율적이지 않습니다.   따라서 개발할 때는 최대한 빌드시스템을 재생성하는 것을 지양해야 하는데, 문제는 “그러면 언제 빌드시스템을 재생성해야 하는가?”입니다.   CMakeCache.txt파일을 재생성해야 하는 경우가 그렇습니다. CMakeCache.txt는 언제 재생성해야 할까요?   Modern CMake 앞에서 설명한커맨드와 개념들은 CMake의 지향점을 기준으로 설명된 내용입니다. 따라서 막상 CMake로 구성된 오픈소스 프로젝트를 접하게 되면, 위에서 사용하는 커맨드와는 다른 방식으로 구현된 것을 자주 보시게 될 것입니다.   CMake도 긴 역사를 가지고 있습니다. 그 과정 속에서 언어도 많은 변천을 겪었죠. 이전에 사용하던 커맨드들은 여러가지 문제점을 야기했는데, 첫 번째로 CMakeLists.txt 파일에 정의된 변수의 스코프가 항상 글로벌로 설정되어, 타겟을 여러 개 정의하는 프로젝트에서 알 수 없는 사이드 이펙트를 발생하게 되는 것입니다. 두 번째로는, 특정 툴채인에 의존성이 강하기 때문에, 컴파일러가 바뀌거나, 빌드시스템이 변경될 경우 작성한 CMakeLists.txt파일이 정상동작하지 않는 상황이 오는 것이죠. 예를 들어, 컴파일 옵션으로 “-std=c++11″처럼 하드코딩된 문자열 형태로 값을 설정하는 경우, 컴파일러가 변경되면 옵션 문법이 달라지면서 동작하지 않는 경우가 있다. 또한 CMake로 생성된 make 빌드시스템을 make 명령어로 빌드하는 것은 make빌드시스템에 대한 의존성을 만들어서 차후 다른 빌드시스템으로 변경할 때 코스트가 따르기 마련이다. 그래서 다음처럼 cmake를 이용해서 빌드명령을 하는 것이 더 유연한 방법이다. cmake --build . 이처럼 현재 CMake문법은 과도기에 있다고 볼 수 있고, 레거시 문법과 현대 문법이 공존하는 상황이라 개발자에게 혼란을 주고 있는 상황이죠. 다행히도, 이러한 문제점을 해결하여 많은 개발자들이 좀 더 모듈화된 CMake 프로젝트를 운영하도록 돕고자 “Effective Modern CMake”라는 발표를 하고 좋은 문법을 공유하려는 시도가 있었습니다. 아래는 그 내용을 요약한 글입니다.    FAQ LLVM을 이용하면 윈도우용 라이브러리도 빌드할 수 있을까요? 이론적으로는 가능하지만, 아직 해결해야 할 난제들이 몇 개 있는 상황이라네요. 그 문제들은 다음과 같아요. 플랫폼 라이브러리와 헤더가 필요합니다. 즉 윈도우 라이브러리와 헤더가 있어야 라이브러리를 빌드할 수 있습니다. 일반적으로 LLVM 프로젝트는 lld라는 링커를 포함하고 이 링커는 크로스플랫폼 링커라고 하네요. 이 링커를 사용한다면, 문제가 없겠지만, OSX에서는 아직도 Clang 링커로 ld를 사용하고 있다네요. 마찬가지로 윈도우 Clang도 기본값으로 link.exe 가 사용된다고 합니다.   참고로 예전에는 C++ name mangling와 RTTI 지원 이슈가 있었지만 요즘에는 해결됐다고 하네요. Linux 에서 윈도우용 라이브러리를 빌드하고 싶다면 어떻게 해야 하는가? 다른 컴파일러로 빌드하고 싶을 때 고려해야 하는 요인은 다음과 같습니다. 타겟플랫폼의 라이브러리, executable 파일 포맷을 컴파일러에서 지원하는가? 플랫폼 라이브러리와 헤더가 있는가? C Runtime Library가 있는가? GCC는 윈도우용 라이브러리 포맷을 지원하지만, 플랫폼 라이브러리와 헤더는 지원하지 않습니다. 따라서, MingW 또는 MingW-w64에서 지원하는 플랫폼 라이브러리를 가져와야 합니다.     레퍼런스 Compilation The C++ compilation process  What Every Computer Programmer Should Know About Windows API, CRT, and the Standard C++ Library – CodeProject https://www.codeproject.com/Articles/22642/What-Every-Computer-Programmer-Should-Know-About-W dll – Difference between load-time dynamic linking and run-time dynamic linking – Stack Overflow https://stackoverflow.com/questions/2055840/difference-between-load-time-dynamic-linking-and-run-time-dynamic-linking c++ – How does the Import Library work? Details? – Stack Overflow https://stackoverflow.com/questions/3573475/how-does-the-import-library-work-details Clang C++ Cross Compiler – Generating Windows Executable from Mac OS X – Stack Overflow https://stackoverflow.com/questions/23248989/clang-c-cross-compiler-generating-windows-executable-from-mac-os-x Are C++ applications cross-platform? – Stack Overflow https://stackoverflow.com/questions/33238345/are-c-applications-cross-platform   Object Codes compilation – What’s an object file in C? – Stack Overflow https://stackoverflow.com/questions/7718299/whats-an-object-file-in-c Compiler Design | Introduction of Object Code – GeeksforGeeks https://www.geeksforgeeks.org/compiler-design-introduction-object-code/ Object Files and Symbols – Nick Desaulniers http://nickdesaulniers.github.io/blog/2016/08/13/object-files-and-symbols/ Demangling C++ Symbols https://fitzgeraldnick.com/2017/02/22/cpp-demangle.html   Windows MinGW & MinGW-w64  Cygwin vs MinGW · snowdeer’s Code Holic http://snowdeer.github.io/c++/2017/07/16/cygwin-vs-mingw/ windows minimalist Cygwin과 MinGW의 차이점은 무엇입니까? / not working https://src-bin.com/ko/q/bc6ac   Web Assembly란? WebAssembly https://webassembly.org/ Creating WebAssembly-powered library for modern web – By https://hackernoon.com/creating-webassembly-powered-library-for-modern-web-846da334f8fc 웹어셈블리 | MDN https://developer.mozilla.org/ko/docs/WebAssembly WebAssembly, the Journey — What is WASM? – By https://hackernoon.com/webassembly-the-journey-what-is-wasm-caf9871108aa  

2019.10.22
after-image
윌슨의 개발지식- 웹스트리밍

윌슨의 개발지식 웹 스트리밍   웹 생태계 웹 생태계 용어부터 정리하고 갈까요? WebAPIMozilla Foundation이 웹 앱과 웹 컨텐츠가 기기의 하드웨어 접근하고 기기의 데이터 저장소에 접근할 수 있도록 해주는 기기호환과 접근 API 스펙.*Web Audio API와는 일련의 관계도 없는 듯… polyfillHTML에서 제공하는 API 중 특정 브라우저에서 아직 지원하지 않는 기능을 워크어라운드로 API를 구현하는 써드파티 라이브러리 active x control한 마디로, 웹 브라우저를 통해 사용자 PC에 프로그램을 깔고, 그 웹사이트에 접속했을 때 그 프로그램을 실행해서 컨트롤 하는 기능 hls.js (참고)HTML 5의 video 엘레멘트와 호환해서 HTTP Live Streaming을 지원하는 Javascript 라이브러리. 따라서 따로 비디오 플레이어가 필요없다. fragmented mp4 (참고)MP4 컨테이너 자체는 헤더에 길이를 명시하고 있기 때문에 스트리밍할 수 있는 포멧이 아니다. 이를 해결하기 위해 만들어진 포맷.   미디어 스트리밍(Streaming)이란? 클라이언트(ex, 브라우저) 로컬드라이브에 파일을 다운로드 하지 않고, Viewer 또는 Listener에게 미디어를 전달하는 방식. 따라서, Progressive Download는 Streaming이 아니다.   프로그레시브 다운로드(Progressive Download)란? 말 그대로 미디어 파일을 다운 받으면서 플레이하는 방식을 말한다.이 때, 유저의 로컬에 파일이 다운로드되기 때문에, 유료 디지털 컨텐츠에 대한 보안이 되지 않는다. Progressive Download의 한계를 정리해 보면, 디지털 컨텐츠 보안 Adaptive Bitrate Streaming 미지원 Seeking 미지원 그런데 Seeking 미지원 문제를 해결하기 위해서 Pseudo Streaming이 사용된다.   스트리밍 포맷(Streaming Format) 스트리밍 포맷은 아래 3가지 구성요소로 결정된다. Protocol Transport, a.k.a. Transport Container Codec 예로, 이에 따르면 HLS 스트리밍 포맷이란, Protocol: HTTP Transport: MPEG-TS Codec Video: h264 Audio: aac and mp3   브라우저에서 스트리밍 미디어 컨텐츠를 재생하려면? 브라우저에서 스트리밍 미디어 컨텐츠를 재생하려면 다음이 필요하다. 해당 브라우저용 미디어 스트리밍 플레이어 미디어 스트리밍 서버 해당 브라우저에서 미디어 스트리밍 플레이어를 구현하기 위한 핵심은 다음과 같다. 외부에서 전송되는 미디어 스트리밍 데이터를 디코딩할 수 있다. 디코딩된 데이터를 비디오 버퍼에 추가할 수 있다. 여기서 1번의 경우는 브라우저마다 디코딩할 수 있는 코덱이 다 다르다. 2번의 경우는 3가지 경우를 생각해 볼 수 있다. 표준 단체인 Mozilla의 API인 MSE(Media Source Extensions)를 사용 *MSE의 개념에 대해서는 여기 참고 브라우저 독자적인 API를 사용 없음     팁들 W3C에서 standard 다음의 4가지 스테이지를 거쳐서 완성된다. Working Draft (WD), Candidate Recommendation (CR), Proposed Recommendation (PR), and W3C Recommendation (REC)   유용한 툴 HTML5test – How well does your browser support HTML5? https://html5test.com/ ECMAScript 6 compatibility table https://kangax.github.io/compat-table/es6/ Can I use… Support tables for HTML5, CSS3, etc https://caniuse.com/#feat=audio-api   참고자료 Demystifying HTML5 Video Player – Eyevinn Technology – Medium https://medium.com/@eyevinntechnology/demystifying-html5-video-player-e480846328f0

2019.10.22