vcpkg?

vcpkg는 Microsoft에서 발표한 C/C++ 패키지 매니저입니다.
vcpkg의 레포에 등록된 패키지들을 install을 통해 손쉽게 설치하고 이용할 수 있습니다.

vcpkg 설치

vcpkg는 전역으로 사용할 수 있지만 이번 글에서는 프로젝트 내에서 사용하도록 설정하겠습니다.

mkdir prac
git clone https://github.com/microsoft/vcpkg.git

prac 폴더를 생성한 후, vcpkg를 클론합니다.
클론하면 vcpkg 폴더가 생성되고, 최초 1회 bootstrap-vcpkg.sh(or 윈도우의 경우, bootstrp-vcpkg.bat)을 실행해야 합니다.

cd vcpkg
./bootstrap-vcpkg.sh

그럼 vcpkg 폴더에 vcpkg 파일이 생성됩니다.
이 파일을 실행함으로 패키지를 설치할 수 있습니다.

패키지 설치

저는 ffmpeg를 설치해보겠습니다.

./vcpkg install ffmpeg

저는 맥이라 그런지, nasm을 설치하라고 떠서 brew install nasm으로 설치했습니다.

-- Performing post-build validation
Stored binaries in 1 destinations in 7.9 s.
Elapsed time to handle ffmpeg:arm64-osx: 1.9 min
Total install time: 1.9 min
To use ffmpeg add the following to your CMake project:

    find_package(FFMPEG REQUIRED)
    target_include_directories(main PRIVATE ${FFMPEG_INCLUDE_DIRS})
    target_link_directories(main PRIVATE ${FFMPEG_LIBRARY_DIRS})
    target_link_libraries(main PRIVATE ${FFMPEG_LIBRARIES})

설치가 끝나면 위와 같은 내용이 출력됩니다.
설치가 잘 되었고, 하단 4 라인을 CmakeLists.txt에 추가하여 사용하면 된다는 뜻입니다.
하지만 저희는 cgo를 사용할 것이기에 다른 메뉴얼을 참고해야합니다.

수동 통합

vcpkg는 수동 통합 문서를 통해 includelib 폴더를 지정해 주는 방법을 소개하고 있습니다.
cgo에서는 어쩔 수 없이 이 방식을 채택했습니다.

cgo

cgo는 익히 아시다시피 go 언어에서 C 코드를 실행할 수 있게 도와주는 도구입니다.

프로젝트 생성

먼저 prac 폴더로 돌아가서 go 프로젝트를 생성합니다.

go mod init prac

그리고 main.go 파일을 생성합니다.

package main

import "C"

func main() {}

cgo를 사용할 것이기 때문에 import "C" 구문을 추가합니다.
그리고 빌드할 때, cgo 관련 플래그를 추가할 수 있지만, 저는 go build만 사용해서 빌드하고 싶으므로 미리 플래그를 추가하겠습니다.

package main

/*
#cgo LDFLAGS: -L./vcpkg/installed/arm64-osx/lib
#cgo CFLAGS: -I./vcpkg/installed/arm64-osx/include
*/
import "C"

func main() {}

#cgo 구문을 통해 LDFLAGSCFLAGS를 추가했습니다.
이제 빌드할 때, 별도의 플래그 없이 해당 include 폴더와 lib 폴더를 참조할 수 있습니다.

vscode c 설정

C 헤더를 가져오는 건 잘 되겠지만, vscode에서 인식하지 못 하면 많이 불편할 것입니다.
아님 말구요.

저는 자동 완성과 인텔리센스가 없으면 안 되는 몸이 되어버렸기 때문에 관련 설정을 해주겠습니다.

vscode에서 커맨드 팔레트를 열어서 C/C++: Edit Configurations (JSON)을 선택합니다.
그럼 프로젝트 내에 .vscode 폴더 밑에 c_cpp_properties.json 파일이 생성됩니다.
해당 json 파일은 제 맥을 기준으로 아래와 같았습니다.

{
    "configurations": [
        {
            "name": "Mac",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [],
            "macFrameworkPath": [
                "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
            ],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c17",
            "cppStandard": "c++17",
            "intelliSenseMode": "macos-clang-arm64"
        }
    ],
    "version": 4
}

이 json 파일에서 "includePath" 내부에 vcpkg 내부의 include 폴더를 추가합니다.

{
    "configurations": [
        {
            "name": "Mac",
            "includePath": [
                "${workspaceFolder}/**",
                "./vcpkg/installed/arm64-osx/include"
            ],
            "defines": [],
            "macFrameworkPath": [
                "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
            ],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c17",
            "cppStandard": "c++17",
            "intelliSenseMode": "macos-clang-arm64"
        }
    ],
    "version": 4
}

그럼 vscode에서 C 헤더파일들을 인식해서 cgo 내에서도 도구의 힘을 빌릴 수 있습니다. hooray!

크로스플랫폼 설정

vcpkg 내부의 installed 폴더 아래는 플랫폼 별로 폴더가 나뉘어져 있습니다.
흔히 쓰이는 실리콘 맥의 경우엔 arm64-osx이고, amd64 윈도우의 경우엔 x64-windows입니다.
그리고 amd64 리눅스는 x64-linux입니다.

맥만 생각하고 작성했기에 방금 main.go같은 형식이 나올 수 있었지만, amd64 windows와 linux에 대해서도 별도의 설정 없이 동작할 수 있게 파일을 생성하겠습니다.

  • osx.go
//go:build darwin

package main

/*
#cgo LDFLAGS: -L./vcpkg/installed/arm64-osx/lib
#cgo CFLAGS: -I./vcpkg/installed/arm64-osx/include
*/
import "C"
  • windows.go
//go:build windows

package main

/*
#cgo LDFLAGS: -L./vcpkg/installed/x64-windows/lib
#cgo CFLAGS: -I./vcpkg/installed/x64-windows/include
*/
import "C"
  • linux.go
//go:build linux

package main

/*
#cgo LDFLAGS: -L./vcpkg/installed/x64-linux/lib
#cgo CFLAGS: -I./vcpkg/installed/x64-linux/include
*/
import "C"

빌드 컨트랙트를 통해 각 플랫폼 별로 적용될 플래그를 다르게 설정하였습니다.

ffmpeg 사용해보기

이제 ffmpeg를 사용해보겠습니다.

package main

/*
#cgo LDFLAGS: -L ./vcpkg/installed/arm64-osx/lib -lavcodec -lavformat -lavutil
#cgo CFLAGS: -I ./vcpkg/installed/arm64-osx/include

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
*/
import "C"
import (
	"fmt"
)

func main() {
	fmt.Println(C.avcodec_configuration())
	fmt.Println(C.avcodec_license())
	fmt.Println(C.avcodec_version())
}
0x100117872
0x100117eac
3933028

아마 잘(?) 실행됩니다.