ShellScript 와 Makefile

이번에 ShellScript 와 같이 정리해보려는 Makefile 의 경우, 보통 C 로 작성된 소스를 의존성을 기술하여 make 라는 명령어로 컴파일을 도와주는 스크립트다.
요즘 go 에서 makefile 을 활용하는 몇가지 글들을 접하면서 ShellScript 보다 체계적이고 깔끔하게 느껴져서 이제는 대부분을 makefile 로 자동화 작업을 하고 있다.
이 글에서는 ShellScript 와 Makefile 어느 것이 더 좋다는 의견 보다는 개발에 사용하는 자동화 템플릿을 만들어 보았을 때, 그 구현이 어떻게 다른지 정도를 정리해본다.

자동화 스크립트에 어떤 것을 기대하나

내가 자동화 스크립트에 기대하는 바나 필요한 기능들을 적어보자.
1. 자동화 스크립트 도움말 표시
: 어플리케이션 사용자에게 가이드 역할을 해주며, 특정 어플리케아션 명령어나 스크립트의 기본이라 생각한다.

2. Docker 컨테이너의 시작/종료
: 내게 개발을 위해 꼭 필요한 도구이며 단순히 생성/시작/종료면 된다. 

3. 어플리케이션의 시작/종료/프로세스 확인
: 이 부분은 디버깅을 위한 부분이라 단순히 수동 시작, 종료, 프로세스 상태 확인 정도면 만족한다. 

4. 어플리케이션 컴파일
: 컴파일이 필요한 언어의 경우, 배포를 위한 빌드기능이 있어야 한다.

5. APIDOC, 개발문서 생성
: 언어를 막론하고 APIDOC 자체가 내게는 꼭 필요한 솔루션이고 새로운 버전을 빌드할때 개발 문서가 같이 갱신 되었으면 한다.

6. 기타
: 특정 언어나 각자 개발 스타일에 따라 필요한 것들. 의존성 관리나 TDD 에 필요한 것들. 

자동화 스크립트 템플릿 작성하기

처음 프로그램을 접하는 사람에게 혹은 해묵은 프로젝트를 다시 들여다 볼 때, 어떻게 사용해야 할지 알려주는 도움말 역할을 해준다. 이를테면 아래와 같은 모습의 도움말.

ShellScript 와 Makefile 로 이러한 도움말을 표시하기 위한 소스 자체가 하나의 ::자동화 템플릿::이 아닐까 싶다.  완성된 도움말에 간단히 각자 원하는 기능을 넣으면 그만이니까.

간단히 Makefile 과 ShellScript 로 아래와 같은 결과물을 내는 스크립트를 작성했다. 뼈대를 만들고 하나 하나 살을 붙여보자.

Makefile 템플릿 구현

템플릿 도움말 구현

PRJ_NAME=$(shell basename "$(PWD)")
PRJ_DESC=<$(PRJ_NAME)>는, 자동화를 위한 템플릿을 만들어보는 프로젝트입니다.\\n 만들어 놓으면 언젠가 또 쓰겠지.

PRJ_BASE=$(shell pwd)
.DEFAULT: help
.SILENT:;

##help: 도움말 (기본명령)
help: Makefile
	echo ""
	echo " $(PRJ_DESC)"
	echo ""
	echo " 사용 방법:"
	echo ""
	echo "	make 실행명령"
	echo ""
	echo " 실행 명령:"
	echo ""
	sed -n 's/^##/	/p' $< | column -t -s ':' |  sed -e 's/^/ /'
	echo ""

##all: 모든 실행파일을 빌드하며, 새로운 개발문서를 작성.
all: 
  # 빌드 명령
	echo "  >  Compile Done."

간단히 설명을 붙이자면, 위 소스에서 help: all 부분이 명령어 선언이고, 그 이하가 명령어 묶음 정도로 보면 된다. (함수명과 함수 본문으로 봐도 좋다.)  help: 오른쪽에 위치한 Makefile 은 종속성을 의미하며 Makefile 이 있어야 help 라는 명령을 실행 할 수 있다는 정도로 보면 된다. 자세한 Makefile 가이드는 아래 링크에 모아 두었으니 참고하면 된다.

도움말을 만들기 위해 만들어진 또 하나의 규칙중 하나가 ##help: 도움말 이 부분인데, help 소스의 sed 명령행을 보면 파일내 ## 주석을 스페이스로 치환하고 컬럼으로 열을 맞추는 부분이 있는데, 이 부분이 위에 이미지로 소개했던 결과물 이미지처럼, 스크립트 내 ## 로 시작하는 주석을 이쁘게 명령어 도움말로 정리해 주는 부분이다.

새로운 명령어(함수)의 추가는 다음과 같이 도커 사용에 대한 예시를 추가 해두었다.

# 간단히 도커 컨테이너에 관련된 디렉토리와 파일를 변수로 묶었다
DOCKER_BASE=$(PRJ_BASE)/build/docker
DOCKER_CFG=$(DOCKER_BASE)/docker-compose.yaml

## d-create: 새로운 도커 컨테이너를 만듭니다. 
d-create: $(DOCKER_CFG)
	cd $(DOCKER_BASE); docker-compose create; cd ${PRJ_BASE}

## d-start: 도커 컨테이너를 시작합니다. 
d-start: $(DOCKER_CFG)
	cd $(DOCKER_BASE); docker-compose start; cd ${PRJ_BASE}

## d-stop: 도커 컨테이너를 정지합니다. 
d-stop: $(DOCKER_CFG)
	cd $(DOCKER_BASE); docker-compose stop; cd ${PRJ_BASE}

기능 추가를 위한 간단한 Makefile 문법

- 변수 반복
NUMBERS=srv.service.js srv.chat.js
loopvar:
	$(foreach number, $(NUMBERS), echo ">> start" $(number) &)
* 명령어 반복
loopcmd: apple banana orange

apple:
	echo "apple cmd"

banana:
	echo "banana cmd"

orange:
	echo "orange cmd"

ShellScript 템플릿 구현

템플릿 도움말 구현

#!/bin/bash

PRJ_NAME=$(basename "$(PWD)")
PRJ_DESC="<${PRJ_NAME}>는, 자동화를 위한 템플릿을 만들어보는 프로젝트입니다.\n 만들어 놓으면 언젠가 또 쓰겠지."
PRJ_BASE=$(pwd)

_CMD=$1;

case "$_CMD" in
##help: 도움말 (기본명령)
"help")
	echo ""
	echo -e " ${PRJ_DESC}"
	echo ""
	echo " 사용 방법:"
	echo ""
	echo "	make 실행명령"
	echo ""
	echo " 실행 명령:"
	echo ""
	sed -n 's/^##/	/p' $0 | column -t -s ':' |  sed -e 's/^/ /'
	echo ""
    ;;
##all: 모든 실행파일을 빌드하며, 새로운 개발문서를 작성.
"all")
    # 빌드 명령
	echo "  >  Compile Done."
    ;;
"d-stop")
    cd $(DOCKER_BASE); docker-compose stop; cd ${PRJ_BASE}
    ;;
"d-start")
    cd $(DOCKER_BASE); docker-compose start; cd ${PRJ_BASE}
    ;;
*)
    $0 help
    exit
esac

ShellScript 도 크게 다르지 않다. 템플릿 구조상의 차이점은 Makefile 과 다른 점은 case 문으로 명령어를 분기했다는 것이고, Makefile 보다는 더 익숙한 편이지만, 다양한 기능을 가진 자동화 스크립트를 만들기에는 좀 장황해 질 수 있겠다.

기능 추가를 위한 간단한 ShellScript 문법

- 변수 반복
'loopvar')
	_PROCESS_LIST=("srv.service.js" "srv.chat.js" "srv.cron.js" "srv.gate.js" "srv.api.js")

	for name in ${_PROCESS_LIST[@]}
  {
      echo ">> start $name";
  }
    ;;
* 명령어 반복
'loopcmd')
	$0 apple;
	$0 banana;
	$0 orange;
	;;
'apple')
	echo 'apple';
	;;
'banana')
	echo 'banana';
	;;
'orange')
	echo 'orange';
	;;

Notes for new Make users
make와 Makefile - 멍멍멍
코코넛냠냠 :: Makefile 문법 요약
Golang: Don’t afraid of makefiles - Radomir Sohlich