Go 용 기본 Makefile 예

Go 소스의 빌드를 편리하게 할 수 있도록 기본적인 Makefile을 작성해 보았다.


Go 특정 버전 이후로는 디폴트로 Module 모드로 동작되어 빌드시에 조금 불편한 면이 있다. 심지어 아주 가끔씩 Go 소스를 작성하고 빌드하다 보니, 빌드 방법이 순간적으로 떠오르지 않기도 하였다. 😕
그래서 누구나 쉽게 바로 빌드할 수 있도록 프로젝트 안에 Makefile 파일을 작성하여 여기서 Module 모드를 처리하고 빌드되도록 구성하였다.

Go 패키지 관리

기존의 GOPATH 만을 사용하여 의존성 관리 및 개발을 진행할 때는 모든 소스코드가 $GOPATH/src 디렉토리에 위치해야만 했다. 이 때는 src의 하위 디렉토리 경로가 모듈(패키지)의 이름이 된다.
이 GOPATH 방식의 가장 큰 단점은, 개발을 위한 workspace가 GOPATH에 고정되어 버린다는 점이다. 예를 들어 Go로 개발하고 있는 프로젝트가 여러 개인 경우에, 모든 프로젝트의 workspace를 단일 GOPATH로 설정하면, 각 프로젝트의 의존성 패키지가 서로 섞이게 되어 의존성 관리가 불가능해지는 상황이 발생할 수 있다.

그래서 GOPATH 만을 사용했을 때의 의존성 문제를 해결하기 위하여 Go 버전 1.11부터 모듈 의존성 관리를 위해 Go Module이 도입되었다.
Go Module 기능은 GO111MODULE 환경 변수를 통해 활성화 할 수 있는데 (이름에 들어있는 111은 Go 버전 1.11을 의미하는 듯), 이 변수는 아래와 같이 3가지의 값 중의 하나를 가질 수 있다. (환경 변수로, 필요한 경우에 변경 가능)

  • GO111MODULE=on (또는 설정하지 않은 경우): Go Module 기능을 사용한다.
  • GO111MODULE=off: Go Module을 사용하지 않는다. 즉, 기존 GOPATH 방법으로 동작한다.
  • GO111MODULE=auto: 현재 위치가 $GOPATH/src/ 디렉토리의 하위 디렉토리라면 GOPATH의 방법으로 동작한다. 만약 현재 위치가 $GOPATH/src/ 디렉토리가 아닌 외부 디렉토리라면 Go Module 방법으로 동작한다.

특히 Go 버전 1.13 버전 이후로 디렉토리에 go.mod 파일이 있으면 기본적으로 Module 모드로 동작한다. (GO111MODULE 환경 변수는 따로 세팅할 필요가 없음)

Go Module 사용시의 경로

Go Module로 사용하는 환경에서는 go get 명령으로 패키지를 다운로드하면 $GOPATH/pkg/ 디렉토리에 저장된다. 실제로 패키지는 $GOPATH/pkg/mod/ 디렉토리 밑에 각각의 버전에 따라 설치된다.
Go Module을 사용하게 되면 프로젝트 소스는 $GOPATH/src/ 디렉토리 이외의 아무 원하는 경로에서나 프로젝트 디렉토리를 만들 수 있다.

Go Module을 사용하는 경우에는 상대 경로의 패키지를 import 할 수 없다. 따라서 만약 상대 경로에서 패키지를 import 하고 싶으면 아래와 같이 GO111MODULE 환경 변수를 auto 값으로 세팅하면 된다.

$ go env -w GO111MODULE=auto

Go Module 관련 파일

  • go.mod

    파일 모듈을 정의하고 종속성 정보를 저장하고 있는 파일이다. Go Module은 이 파일을 통해 패키지들을 관리하는데, module, require, replace, exclude의 4가지 키워드를 사용한다. 프로젝트 소스를 외부에 배포하는 경우에, 이 go.mod 파일을 함께 배포할 수 있다. (이렇게 되면 본인이 빌드에 사용했던 각 패키지의 버전을 그대로 사용하게 함)

  • go.sum

    파일 go.mod에 종속성 정보가 추가될 때 생성된다. 이 파일은 설치된 모듈의 해시 값을 저장해두고, 매 go 커맨드가 실행되기 전에 설치 되어있는 모듈의 해시 값과 go.sum에 저장된 해시 값을 비교하여 설치된 모듈의 유효성과 수정된 항목이 있는지 검사한다.

Go Module 관리

  • 모듈이나 app에서 사용하는 모든 모듈의 의존성 정보를 아래와 같이 확인할 수 있다.
    $ go list -m all
    
  • 아래 예와 같이 특정 패키지의 사용 가능한 버전을 얻을 수 있다.
    $ go list -m -versions github.com/zserge/lorc
    
  • 특정 버전의 패키지는 아래 예와 같이 다운받을 수 있다. (패키지명 뒤에 @ 뒤에 버전 정보)
    $ go get github.com/zserge/lorc@0.1.9
    
  • 사용하지 않는 모듈의 의존성 제거는 아래와 같이 하면 된다.
    $ go mod tidy
    

Makefile 기본 템플릿

아래 예와 같이 Makefile 파일을 작성하여 make 명령으로 모듈 처리 및 빌드할 수 있도록 구성하였다. (참고로 나는 보통 빌드된 실행 파일을 upx를 이용하여 크기를 줄이는데, 아래 예에서는 생략하였음)

TARGET = test
SRCS = main.go sub1.go sub2.go

# 전체 빌드
all: module
    go build -ldflags "-s"

# 바로 실행
run: module
    go run $(SRCS)

# 모듈 업데이트
module: go.mod
    go mod tidy

# 모듈 초기화
go.mod:
    go mod init $(TARGET)

# 전체 clean
clean:
    @rm -f go.mod go.sum $(TARGET)

참고로 위에서 -ldflags 옵션에서 -s만 사용하여 strip만 시켰는데, 사실 -w -s 옵션을 사용하면 빌드되는 실행 파일의 크기를 조금 더 줄일 수가 있으나, 간혹 이 경우에 빌드된 실행 파일이 바이러스로 잘못 진단되는 경우가 있어서 여기에서는 위와 같이 -s 옵션만 사용하였다.

이제 이 Makefile을 이용하면 아래와 같이 바로 실행시킬 수 있다.

$ make run

또 아래와 같이 실행 파일을 생성할 수 있다.

$ make

카테고리: ,

업데이트: