112 – make란?

오랜만에 다시 C 글을 작성합니다. 오랫동안 기다렸다면 죄송합니다. 졸업논문 쓰다가 머리가 너무 안돌아서 다시 잡고 하는군요. ㅠㅠ 그럼 시작합니다. ㅇㅂㅇ/

make 파일은 어플리케이션의 구성 방법을 make에 알려주는 텍스트 파일로, 다음과 같이 대상, 의존성, 명령으로 이루어진 규칙이 나열된 형식을 지닌다.

대상(target): 대상에 의존되는 파일 1 [대상에 의존되는 파일 2] [대상에 의존되는 파일 3] …
명령(command)

컴파일 할 파일은 적어도 하나는 있어야 하기 때문에 의존되어 컴파일 될 파일에 대해서는 하나는 꼭 적어주게 된다. 그리고 나서 명령을 한다. 이렇게만 보면 잘 모른다. 그러니 일단 하나하나 살펴보자.

  • 대상

대상(target)은 make가 궁극적으로 생성하는 것이다. 일반적으로 오브젝트 파일이나 실행 파일이 된다. 아래에 단순한 수준으로 예제를 하나 만들어보았다.

test: test.c
gcc test.c -o test

우리가 항상 뭐 하나 둘 만들어보고 돌려볼 때 쓰던 코드다. test라는 대상을 만들꺼라서 test를 직접 작성하였다.

그러나, 실제로 개발하다 보면 저기에 오는 것이 오브젝트 파일이나 실행 파일이 오는 것은 아니다. 특정 레이블이 오기도 한다. 레이블이 오는 경우에는 이런 경우다.

clean:
rm test.o

이렇게 만들면 make clean 이라고 명령하면 오브젝트 파일인 test.o를 삭제하는 clean이 실행된다. 이건 레이블을 설명할 때 좀 더 설명하도록 하겠다만 일단은 레이블이 올 수도 있다는 것을 알아두자.

  • 의존성

의존성(dependency)은 대상과 대상을 생성하는 데 필요한 소스 파일의 관계로, make 파일에서는 대상과 대상을 생성하는 데 필요한 파일 목록을 클론(:)으로 구분하여 작성을 하여 준다. 이것도 예제를 보면 쉽게 이해할 수 있다.

test: test1.o test2.o test3.o
test1.o: test1.c a.h
test2.o: test2.c a.h b.h
test3.o: test3.c b.h c.h

첫 줄에는 test라는 대상을 생성하는 데 test1.o, test2.o, test3.o 세 파일이 필요한 것을 알 수 있다. 그리고 두번째 줄에서 test1.o를 생성하는데 test1.c, a.h가 필요하고, 세번째 줄에서 test2.o를 생성하는 데 test2.c a.h b.h가, test3.o를 생성하는 데 test3.c b.h c.h가 필요하다는 것도 알 수 있다. 그리고 이 4줄의 코드가 전부 의존되는 파일들이 이어져 있다는 것을 알 수 있다. 이게 바로 의존성이다. 한 개 이상의 파일들이 서로를 필요로 하고, 그에 맞게 연결되어 있는 것이다. 이 경우에는 의존 관계 트리 형태로 최종 test를 만드는 데 필요한 헤더와 소스코드 파일이 트리 구조로 이어져있을 수 있다.

  • 명령

명령(command)에 정의된 명령은 대부분 컴파일러 호출이고, 대상이 의존하는 파일 중 변경된 파일이 있거나 대상이 존재하지 않을 때 실행된다. 명령에는 일반적으로 쉘에서 쓸 수 있는 모든 명령어를 사용할 수 있으며, bash의 기반이 되는 bash 쉘 스크립트도 지원한다. (이거 땜에 솔직히 쉘 프로그래밍을 공부하는 거라고 보면 된다.)

그리고 명령을 사용할 때는 다음과 같이 반드시 탭 문자로 시작해야 한다. make가 명령어인지 아닌지를 구분하는 것이 바로 탭 문자이기 때문이다. 또한 탭 문자 크기만큼 스페이스바로 미는 사람들(특히 초심자들)이 있는데, 그렇게 하면 안된다. 필자는 블로그 글을 쓰기 편하게 하려고 탭 대신에 스페이스로 밀어서 글을 작성하였다. 그러나 실제로는 그렇게 안짠다.

test: test1.o test2.o test3.o
[    탭    ]gcc -o test test1.o test2.o test3.o

이제 이걸 간단한 예시로 알아보려고 한다. 예시에 작성한 내용이 좀 많아서 그렇지 앞에서 설명한 내용대로 움직이는 지 확인하는 예시이다.

우선 test1.c test2.c test3.c 파일을 각각 만들어준다.

20171109_023028

위와 같이 소스코드를 작성한 후, 참조하는 헤더를 touch로 만들어준다. 참고로 stdio.h 헤더 일부러 include 안했다. 나중에 보자. 그리고 터치로 파일 만들어서 내용이 비어 있어도 컴파일 하는 데 있어서 상관은 없다. 파일이 없으면 파일 없는 에러를 내지만, 사용자 헤더 내용이 없는 건 오류가 되지 않는다.

이제 Makefile을 작성한다. vi Makefile 이라고 해서 아래와 같이 작성을 한다. 참고로 짝수줄 앞에 빨간 영역이 바로 탭 영역으로 구분을 한 곳이다.

작성 후, make를 입력하면 명령들이 실행되는 것을 볼 수 있다. 아래와 같이 나오면 정상이다. stdio.h 헤더가 없어서 경고를 출력한 것이다. 파일 자체는 두번째 화면과 같이 다 만들어졌다.

여기서 중요한 것이 있다. 바로 Makefile의 실행 순서이다. 분명히 작성할 때에는 test를 맨 먼저 적었을 텐데, 정작 실행은 test1.o, test2.o test3.o 순서로 실행이 진행되었다.

즉, make가 Makefile에서 설정된 순서대로 실행이 되는 것이 아니라는 것이다. 바로 의존되는 관계 트리에서 가장 아래에 있는 의존성을 먼저 실행하게 된다. 가장 하위 대상에 속한 명령이 실행되고, 그 다음으로 해서 탐색 루트르 따라 올라가서 마지막에 제일 높은 의존을 가진 test가 실행된 것이다.

아까도 이야기를 잠깐 적었지만, 파일이 없는 것은 중요한 오류다. 그렇기 때문에 의존성이 있는 파일의 경우에는 의존성을 체크하여 의존 파일이 만들어져 있는지, 새로 만들어야 하는지를 탐색하면서 순서를 정리하여 빌드해 주는 것이다. 상당히 중요한 내용이고 Makefile이 엄청 커지게 되면 이게 제대로 구분이 가지 않는 경우도 많이 생긴다. 따라서 의존도 체크하는 것은 상당히 중요한 일이다.

또한 make 명령의 경우에는 각각의 타겟을 단독으로 실행할 수 있다. 우선 아래처럼 최종 완성본인 test를 지워보고 나서 make test를 통해 test만 실행해 보았다. 그러면 test는 오브젝트 파일 셋이 그냥 그대로 있으니 바로 컴파일해서 파일을 만들어내면 되는 것이다.

파일이 수정된 경우에 대해서도 알아보자. 우선 b.h 파일에 아까 작성하지 않았던 stdio.h를 선언해주자.

이 다음에 make를 진행하게 되면 b.h 파일이 의존성 파일 목록에 있는 작업들이 다시 실행될 것이다. test1의 경우에는 연관이 없기 때문에 빌드가 되지 않았다.

이번에는 a.h 파일을 수정하였다.

마찬가지로 a.h와 의존되는 파일이 다시 컴파일되었다. 헤더가 완전히 다 들어갔으니 경고도 날라오지 않는다.

참고로 이미 다 컴파일 되어 있는 상태에서 다시 make로 컴파일을 하게 되면 ‘up to date”라는 문장을 보게 된다. 이미 다 업데이트 되었으니 안해도 된다는 것이다.

이제 레이블을 Makefile에 추가해 보겠다. clean이라는 레이블로, 다시 새로 빌드할 때마다 일일이 찾아서 지울 수 없으니 Makefile에 작성하였다.

저 명령이 정확한 것인지 쉘에 직접 입력하여 확인하였다. 오브젝트 파일과 최종 결과물인 test가 지워졌다. 그리고 이걸 다시 make로 생성 후, make clean으로 clean 레이블을 실행하였다. 그러니 삭제 코드가 그대로 실행되는 것을 볼 수 있다.

이것이 바로 make의 기본적인 사용법과 기본적인 내용이었다. 이 외에도 여러모로 알아둬야 하는 추가적인 내용 및 옵션들에 대해서도 정리를 하겠지만, 일단 이전 실습때 작성했던 C 파일들 중에 골라서 별도의 폴더를 만들고, 해당 파일과 Makefile을 만들면서 연습해 봐도 좋다.

오랜만에 작성하니 은근 힘들다…ㅠㅠ (논문 안쓰고 이거 써서 그럼..ㅠㅠ)

“112 – make란?”의 한가지 생각

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.