화요일, 2월 10, 2015

STM32CubeMX-FSMC-eGUI & Touch Panel Demo

STM32CubeMX-FSMC-eGUI Demo

FSMC interface generated by STM32FCubeMX and Freescale's eGUI v3.0 is ported.

- MCU: STM32F103VE @ 72Mhz
- LCD: 2.4" 320x240/ili9325
- Developed under Eclipse Luna with ARM GCC 4.9










eGUI는 프리스케일(Freescale)사에서 개발하여 지금은 공개로 전환된 GUI(Graphic User Interface)라이브러리 입니다.

버튼, 체그박스, 텍스트박스, 메뉴, 애니메이티드 아이콘 등의 객체를 사용할 수 있습니다.
비트맵 이미지 로딩 가능합니다.
비트맵 폰트를 지원합니다.

포팅 하느라 속을 들여다 보니 고급 프로그래밍 기법들이 동원 되었더군요. 소스는 순수히 C 인데 객체 지향적 기법이 적용되었습니다. 각 객체에 콜-백 함수를 지정해 놓았는데 마치 윈도우즈 API 프로그래밍 하는 느낌입니다. 

eGUI 소스를 다운받으려면,


윈도우즈 폰트를 비트맵 폰트용 C 소스 생성해주는 기능, 비트맵 이미지를 C 소스로 변환 해주는 유용한 유틸리티도 제공 됩니다.


금요일, 2월 06, 2015

STM32CubeMX와 Eclipse CDT/GNU 툴을 사용한 예제:LED 켜기 (2편 컴파일 과 빌드)

STM32CubeMX와 Eclipse CDT/GNU 툴을 사용한 예제:
LED 켜기 (2편 컴파일 과 빌드)

3. Eclipse 에서 컴파일
    3-1. 시작 (워크 스페이스)
    3-2. 프로젝트 생성/GNU 툴 체인 선택
    3-3. 인클루드 패스 지정
    3-4. STM32용 디파인 매크로(USE_HAL_DRIVER, STM32F103xE)
    3-5. 불필요한 파일 컴파일에서 제외
    3-6. 스타트-업 코드 명 변경(화일 확장자 .S)
    3-7. 링크 스크립트 적용
    3-8. 빌드 완료

----------------------------------------------------------
3. Eclipse 에서 컴파일
----------------------------------------------------------

3-1. 시작 (워크 스페이스)
-----------------------

Eclipse를 실행 한다. 처음 Workspace 를 지정하라고 나올 것이다. 앞서 STM32CubeMX에서 생성했던 C 코드의 상위 폴더를 지정한다. 프로젝트 폴더를 공유하기 위함이다.

예: CubeMX에서 생성한 폴더가 아래와 같다면,

C:\Users\goodkook\Documents\STM32Cube\GPIO_PB5_LED

Eclipse 의 워크스페이스 폴더는

C:\Users\goodkook\Documents\STM32Cube



3-2. 프로젝트 생성/GNU 툴 체인 선택
----------------------------------

Eclipse에서 프로젝트를 생성한다. 메뉴, File > New > C Project 를 선택하면 프로젝트 생성 창이 나온다.

Project Name을 무엇으로 하던 상관 없으나 CubeMX와 동일한 이름으로 하면 두 도구사이에 한 폴더를 공유할 수 있다.

Project Type은 Executable > Empty Project 로 선택하자. 그 아래 STM32F10X C/C++ Project 라는 항목이 있는데 CubeMX 이전의 라이브러리 소스를 사용한 경우에 해당된다. 또한 복잡한 GCC 와 칩마다 다른 링크 옵션을 골라야 하는데 초보로서 쉽지 않다. CubeMX 가 생성해준 링크 스크립트를 이용하기로 하자.



이어서 "Select Configuration"은 그냥 넘어가고, "Cross GNU ARM Toolchain"은 주의하여 살펴본다.

- "Toolchain Name"에 "arm-none-eabi-gcc"를 확인한다. 바로 앞서 설치했던 GCC의 실행 파일 이름이다.
- "Toolchain Path"는 Browse 버튼을 눌러 앞서 설치한 GCC의 폴더 경로를 지정해준다. "arm-none-eabi-gcc.exe"가 존재하는 bin 폴더까지 지정한다.

아래의 예에서 경로명은,

C:\Program Files (x86)\GNU Tools ARM Embedded\4.9 2014q4\bin



이제 Eclipse의 "Project Explorer" 영역에 CubeMX에서 생성되었던 파일들이 모두 보일 것이다.



3-3. 인클루드 패스 지정
---------------------

메뉴, Project > Build All 해서 무작정 컴파일 해보자. 오류가 난다. 당연하다. 언제 인생이 그리 만만했던가.

무슨 오류인지 살펴보자. 오류 메시지를 잘읽고 적절하게 대처하는 것도 기술자가 갖춰야 할 중요한 덕목이다.

먼저 GNU 답게 빌드 과정(컴파일 과 링크)에서 make 유틸리티가 사용되고 있다.
오류는 c 파일 main.c에서 stm32f1xx.h 를 찾지 못했다고 한다. 컴파일러 환경 변수에 "Incluse Path"를 지정해 주지 않아서 발생한 것이다.



인클루드 패쓰에 지정되어야할 경로는 총 4군데다. 프로젝트 최상위 폴더에서,

/Drivers/CMSIS/Device/ST/STM32F1xx/Include
/Drivers/CMSIS/Include
/Drivers/STM32F1xx_HAL_Driver/Inc
/Inc

메뉴, Project > Properties 를 선택하여 컴파일러 추가설정 창을 띄운다.

+ C/C++ Build > Settings 를 선택하면 "Cross ARM Compiler"아래에 "Includes"를 지정하는 항목이 있다. 브라우즈 버튼으로 위의 네군데 폴더를 추가해준다.



3-4. STM32용 디파인 매크로(USE_HAL_DRIVER, STM32F103xE)
---------------------------------------------------------

컴파일 해보자. 다시 무지막지한 컴파일 에러가 보일 것이다. 좌절하지 말자. 오류를 보니 이상하다. GCC는 범용 C 언어 컴파일러다. GPIOB 같은 ST에서 제공하는 라이브러리에서 정의된 특수한 변수나 자료형을 인식하지 못한다. 아마 .h 파일 어딘가에 정의되어 있을 것이다. C 언어에서 이런 특수한 경우를 처리 할 때 define 매크로를 사용한다.


CubeMX 에서 생성된 매크로 이름은 USE_HAL_DRIVER 이다. GCC 컴파일러에게 이 매크로를 추가해 주자.



다시 컴파일 하면 또 다시컴파일 해보면 엄청난 오류 메시지를 보게된다. 무엇이 문제인지 에러난 파일을 따라가 보자. 해당 파일을 더블 클릭하면 편잡창에 열린다. 에러 위치를 보자.

선택한 마이크로 컨트롤러를 지정하는 매크로 성의가 더 필요하다. 이 예제에서 선택한 마이크로 컨트롤러는 STM32F103VET 였다. 이에 해당하는 매크로는 STM32F103xE 다.





3-5. 불필요한 파일 컴파일에서 제외
--------------------------------

다시 컴파일 해보면 역시 컴파일 오류가 날 것이다. 이번에도 원인을 찾아보자. DSP_Lib에서 발생된 에러다. DSP 라이브러리가 제공된다는 것은 Cortex-M이 DSP용으로 사용할 만큼 성능이 나온다는 반증이 아닐까? 어쨌든 지금은 필요 없으니 컴파일에서 제거해 준다. RTOS 도 필요 없으니 제거해 준다.

사실 HAL 드라이버 소스도 모두 컴파일 할 필요 없다. HAL 이란 Hardware Abstraction Layer 로서 동원된 하드웨어의 초기화와 구동을 위해 마련된 루틴들이다. 이번 예제는 RCC와 GPIO만 사용되었으므로 해당 파일만 컴파일 하면된다. 비록 컴파일 되었더라도 링크시 실행 파일에는 포함되지 않는다. 다만 컴파일 시간이 더 소요된다.


다시 컴파일 빌드 해보자. 또 에러다. 이제 끝이 보이니 인내심을 가지고 찾아보자.

에러 메시지를 보니 링커(Linker)에서 오류가 났다. 오류 메시지를 보니 "HAL_MspInit"라는 함수가 두개나 있다고 한다.

C:\Users\goodkook\Documents\STM32Cube\GPIO_PB5_LED\Debug/../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_msp_template.c:78: multiple definition of `HAL_MspInit'

아래 파일은 코드 생성을 위한 틀에 해당하는 파일이니 컴파일에서 제외 시키자.

/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_msp_template.c



그리고 여러개의 스타트-업 코드가 존재할 필요 없다. STM32F103VE 용 스타트업 코드 한개만 두고 나머지는 제외시키자.



다시 빌드해 보자.

만쉐~~!!!!!
드디어 성공적으로 컴파일 되었다!



어? 그런데 이상하다. 실행 코드 크기가 너무작다. 그리고 바로 위 링크 경고도 보인다.

... arm-none-eabi/4.9.3/../../../../arm-none-eabi/bin/ld.exe: warning: cannot find entry symbol Reset_Handler; defaulting to 08000000

리셋 핸들러 Reset_Handler 가 없단다. 리셋 핸들러를 비롯해 각종 인터럽트 벡터 테이블은 스타트-업 코드에 정의되어 있다. 그렇다면 스타트-업 코드가 빠졌다는 말인데....

3-6. 스타트-업 코드 명 변경(화일 확장자 .S)
----------------------------------------

스타트-업 코드는 여기에 있다. 어셈블러 소스다. 그런데 화일 확장자가 .s 이다. GCC 의 어셈블러는 확장자가 .S로 대문자 이어야 한다. 화일명을 startup_stm32f103xe.S 로 바꾸자.


그리고 다시 빌드 한다. 실행 파일을 생성하기 위해 링크 스크립트가 포함되어야 한다. 링크 스크립트에는 실행코드가 적제될 주소 영역 정보가 포함되어 있으므로 타겟 CPU마다 다르다. STM32CubeMX에서 생성해준 링크 스크립트를 적용해야 한다.



3-7. 링크 스크립트 적용
---------------------

링크 스크립트를 적용하기 위한 옵션은 다음과 같다.

-T "..\TrueSTUDIO\GPIO_PB5_LED Configuration\STM32F103VE_FLASH.ld"




만쉐~~!!!!!
드디어 성공적으로 컴파일 되었다! 



3-8. 빌드 완료
-------------

그런데 별것도 아닌 프로그램이 실행 코드가 너무 크다. 컴파일러 최적화 옵션을 좀 손봐야 하겠다. 앞으로도 몇번의 연습이 필요할 것이다.

어쨌든 칩에 다운로드하고 실행 시켜보자. GDB로 디버깅도 해보고....


STM32CubeMX와 Eclipse CDT/GNU 툴을 사용한 예제:LED 켜기 (1편 STM32CubeMX 사용법)

STM32CubeMX와 Eclipse CDT/GNU 툴을 사용한 예제:

LED 켜기 (1편 STM32CubeMX 사용법)

목차:

1. 준비사항
2. 기본코드 생성(STM32CubeMX 사용법)
    2-1. 시작
    2-2. 클럭 입력 지정
    2-3. 클럭 설정
    2-4. 입출력 핀 지정
    2-5. 소스코드 생성
----------------------------------------------------------------------

STM32CubeMX와 GNU 툴을 사용한 간단한 예제를 만들어보자. C 언어 처음 배울 때 접하는 "Hello World"격에 해당하는 LED 한개 반짝이는 예제다.

이 마이크로 컨트롤러의 내부구조라 던가 성능이 어떻다 던가 그런류의 복잡한 이야기는 차차 알아보기로 하고 일단 되는지 보기로 하자.

-------------------------------------------------------------
1. 준비사항


-------------------------------------------------------------

이 예제의 목표는 GCC라는 C 컴파일러와 Eclipse라는 무료 개발 환경의 활용법을 익히는 것이다. 준비사항은 다음과 같다.

소프트웨어:

STM32 ARM 개발환경(Eclipse/ARM GCC & GDB) 설치
http://goodkook.blogspot.kr/2015/02/stm32-arm-eclipsearm-gcc-gdb.html

프로그래머의 생명을 연장해 주는 STM32CubeMX

하드웨어:

간단한 개발 보드가 있어야 겠다. STM32 마이크로 컨트롤러는 모든 것이 내장되어 있다. 외부에 필요한 것은 JTAG 다운로드를 위한 JTAG과 클럭용 크리스털만 있으면 된다. 내장된 RC 오실레터를 이용하면 이마져도 필요없다.

그외 응용에 소요될 회로가 있을 것이다. 개발용 보드는 하나 만드는 것도 좋지만 요즘은 워낙 다양한 보드들이 싸게 공급되고 있으니 적당한 것을 하나 구입하는 것도 좋겠다. 그래픽 TFT LCD가 달린 것도 5~6만원에 살 수 있다. JTAG 다운로더 디버거가 2~3만원 정도하니까 넉넉하게 10만원 정도 투자하면 되겠다.


-------------------------------------------------------------
2. 기본코드 생성(STM32CubeMX 사용법)


-------------------------------------------------------------

2-1. 시작

STM32CubeMX 를 실행 시켜 프로젝트를 생성 한다. 적용할 마이크로 컨트롤러를 선택하는 창이 나올 것이다. Series는 STM32F1으로 Cortex-M3 라는 계열의 ARM 코어다. 사용할 컨트롤러 종류는 STM32F103, 패키지(Package)는 100핀이다. 이렇게 고른 마이크로 컨트롤러는 STM32F103VET6 다.



2-2. 클럭 입력 지정

이번 예제에서는 외부에서 크리스털로 클럭을 만들어주는 것으로 한다. 클럭 제어를 RCC(Reset and Clock Control) 라고 한다. 칩 외부에서 고속 클럭 공급을 HSE 라고 한다. "Crystal/Ceramic Resonator"를 지정하면 칩의 해당핀에 표시된다.HSE용 크리스털은 8Mhz 짜리를 사용한다. LSE는 저속 클럭으로 내장 시계에 사용되는 클럭으로 보통 32Khz 이다. 이번 예제에서는 사용하지 않는다.


2-3. 클럭 설정

최근 저전력 마이크로 컨트롤러들은 다양한 기능들이 내장된 만큼 내부 클럭 구조가 아주 복잡하다. CPU 코어가 매우 빠른 속도로 작동한다고 주변장치도 같은 속도로 작동할 수는 없다. 플래쉬 메모리의 작동 속도나 시리얼 통신의 장치의 작동은 이에 비해 한없이 느리기 때문이다. 또한 저전력을 구현하기 위해 사용중이 아닌 주변장치는 작동을 멈춰야 하는데 전원을 끌 수는 없고 클럭을 차단한다.

STM32 마이크로 컨트롤러의 내부 클럭구성을 보자. 클럭에 붙은 이름만 해도 참 다양하다. CPU에 사용하는 클럭(FCLK), 주변장치에 사용하는 클럭(HCLK)등으로 나뉘어 있다. 매우 고속의 시스템 클럭(SYSCLK)으로 부터 분주해서(클럭 속도를 낮춰서) 용도에 맞게 공급한다. 속도를 낮추는 기능이 프리 스케일러(Prescaler)다.

가정 먼저 할 일은 시스템 클럭(SYSCLK)을 만드는 방법을 선택하는 일이다.

칩 외부에 크리스털을 사용(HSE)하거나 칩내부에 RC 발진기(HSI)를 사용하는 방법이 있다. (1)번 선택으로 HSE와 HSI중 하나를 고른다.

(2)번 선택은 PLL을 통해 생성된 고속의 클럭(PLLCLK) 혹은 HSE나 HSI에서 발진시킨 클럭을 시스템 클럭으로 사용할 수 있다.

CPU 마다 최대 클럭 주파수가 정해 져 있다. 시스템 클럭을 바탕으로 프리스케일러를 통해 적절한 클럭을 생성한다. 허용된 클럭 주파수보다 높게 설정하면 빨간색으로 표시된다. 프리스케일러 값을 조절해 주도록 한다. 높은 클럭이 공급될 수록 전력량은 늘어나므로 고속이 능사는 아니다. 용도에 맞게 조절해 주자.



2-4. 입출력 핀 지정

이번에는 LED를 켜고 끌 범용 입출력 핀(GPIO, General Purpose Input/Output)에 출력용으로 지정한다. 예제에서는 PB5 핀을 사용했다. PB5 핀 위치에 마우스 클릭 하면 기능을 선택할 수 있다. GPIO_Output을 선택 했다.



2-5. 소스코드 생성

이제 C 소스코드를 생성할 차례다. 그전에 생성될 코드를 저장할 위치와 사용할 컴파일러를 고른다. Toolchain/IDE는 아쉽게도 EWARM, ARM_MDK 그리고 TrueSTUDIO 밖에 없다. 세개 모두 상용 소프트웨어다. 앞으로 사용하려는 Eclipse CDT 는 없다. 하지만 TrueSTUDIO에서 사용하는 컴파일러는 GCC 이므로 이를 선택한다. (사실 MDK나 EWARM 에서 사용하는 컴파일러도 GCC 소스를 가져다 자가용으로 재생성한 것이다. 상용화된 것은 IDE 환경과 편이 도구들 이다.)



이제 Project > Generate Code 메뉴를 선택하면 곧바로 소스코드가 생성된다. 소스 코드가 생성된 폴더는 아래와 같다. Drivers 폴더는 ST의 각종 라이브러리 소스들이 있다. 엄청난 양의 .c와 .h 들이 생성되어 있는데 필요한 것만 골라 사용하기로 한다.





프로그래머의 생명을 연장해 주는 STM32CubeMX

프로그래머의 생명을 연장해 주는 STM32CubeMX

STM32 라는 마이크로 컨트롤러가 있다. ST에서 나온 ARM Cortex-M 계열의 마이크로 컨트롤러인데 칩 하나에 없는게 없다. 그 자랑은 ST 사의 홈페이지나 다른 참고 사이트를 참조하고 일단 좋은 거다.

ST 홈페이지
http://www.st.com

이렇게 좋은 마이크로 컨트롤러도 소프트웨어가 있어야 무슨 일이든 시킬 수 있다. 그런데 좋은 만큼 프로그램 작성하기가 여간 까다로운 것이 아니다. 일단 C/C++로 프로그램을 작성해야 하는데, C언어의 구조체(Structure)가 무지막지하게 사용되고 그 구조도 무척 복잡하다. 이 칩을 동작 시키기위한 초기화 과정을 보기만 해도 그냥 기가 질릴지도 모른다.

그래서 ST사는 STMCube라는 것을 내놨는데 일종의 소스코드 자동 생성기 이다. 그래픽 화면을 보면서 칩의 입출력을 지정해 놓으면 자동으로 C 소스코드를 생성해준다. 소개글을 보니 어렇게 써있다.

STMCube™ is an STMicroelectronics original initiative to ease developers’ life by reducing development efforts, time and cost. STM32Cube covers STM32 portfolio.

개발자의 인생을 편하게 해준다니 믿고 써보자.

STM32Cube includes the STM32CubeMX which is a graphical software configuration tool that allows generating C initialization code using graphical wizards.

이렇게 생긴 화면에서 핀의 기능을 정의하고 내장 기능을 설정해주면 바로 C/C++ 소스코드가 생성된단다.



그래픽 LCD 인터페이스를 위한 FSMC도 포함되어 있다. 미들웨어 항목(MiddleWares)를 보면 RTOS와 파일 시스템(FATFS) 도 있다. 그외 USB, TCP/IP, DSPLib도 포함되어있다. 이런 류의 코드들은 이전부터 ST에서 개별적으로 제공되었는데 이제 CubeMX 라는 유틸리티에 모두 포함되었고 버튼만 누르면 소스 코드가 자동 생성된다.

STMCube 가 나오면서 이전에 제공되던 각종 라이브러리 소스들의 체계가 상당히 변경되었다. 이전에 잘 사용하던 프로그래머라면 약간 짜증 날 지도 모른다. 기본만 안다면 적응하기 어렵지 않을 것이다.

STM32CubeMX 설치

STM32CubeMX 페이지에서 설치화일을 다운 받아 실행시키면 설치는 끝난다.

STM32CubeMX
http://www.st.com/web/en/catalog/tools/PF259242

제아무리 좋은 도구가 있어도 사용할 줄 알아야 한다. 간단하게 뭐라도 해보기로 하자.







STM32 ARM 개발환경(Eclipse/ARM GCC & GDB) 설치

STM32 ARM 개발환경(Eclipse/ARM GCC & GDB) 설치

1. ARM GCC 컴파일러 설치

아래 사이트에서 GCC 컴파일러를 다운 받아 설치한다.

GNU Tools for ARM Embedded Processors
https://launchpad.net/gcc-arm-embedded/

GCC의 최신 버젼은 4.9-2014-q4-major 이다. 다운로드 주소는 다음과 같다.

2. Java Run-Time 설치

Eclipse 개발 통합 환경은 Java기반에서 작동하므로 Java Run-Time Environment를 다운 받아 설치한다.

http://java.com/ko/download/manual.jsp

3. Eclipse CDT

Eclipse는 통합 개발 환경(IDE) 이다. 현재 최신버젼은 "Luna"이다.

Eclipse
http://www.eclipse.org/downloads/packages/release/Luna/R

C/C++ 개발용 통환 환경 설치화일을 다운 받는다.

Eclipse IDE for C/C++ Developers
http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/lunar

일반 Zip 압축파일로 배포되므로 적당한 위치에 압축을 풀면 된다.

4. Eclipse CDT용 플러그 인

Eclipse는 C/C++뿐만 아니라 다양한 프로그래밍 언어 및 대상 플랫폼개발환경을 지원한다. ARM용 GCC를 위한 플러그-인(보조도구들)을 추가로 설치한다.

4-1. Eclipse를 실행 후,
4-2. 메뉴 항목, Help > Install New Software 선택 하면 "Install"창이 나타난다.
4-3. "Available Software Site"를 누르면 여러 사이트 주소창이 나오는데, CDT를 체크
4-4. "Install"창의 "Work With"에서 아래 항목 선택

     "CDT - http://download.eclipse.org/tools/cdt/releases/kepler"

4-5. 잠시 기다리면 CDT Main Features와 CDT Optional Features 가 나올 것이다.

    CDT Main Features 에서,
      - C/C++ Development Tools 선택

    CDT Optional Features 에서 아래 항목들을 선택
      - C/C++ Autotools support
      - C/C++ C99 LR Parser
      - C/C++ Cross Compiler Support
      - C/C++ GDB Hardware Debugging
      - C/C++ Memory View Enhancements
      - C/C++ Miscellaneous Utilities


4-6. 이어서 몇번의 Next 버튼을 눌러 설치를 진행한다. 인터넷에서 파일은 다운 받아야 하므로 시간이 조금 걸린다.

4-7. ARM GCC 디버거(Debugger) gdb 를 사용할 수 있다. Eclipse 환경에서 소스 디버깅이 가능하다. ARM GDB를 위한 플러그-인을 설치한다.

Eclipse 의 메뉴, Help > Install New Software 에서, "Add..." 버튼을 눌러 아래의 주소를 입력한다.

    http://gnuarmeclipse.sourceforge.net/updates

잠시후 GNU ARM C/C++ Cross Development Tool 가 나올 것이다. 전부 선택하고 설치한다.

참조 사이트:
GNU ARM Eclipse
A family of Eclipse CDT extensions for GNU ARM development
http://gnuarmeclipse.livius.net/blog/plugins-install/


컴파일한 바이너리 파일을 컨트롤러 칩에 다운로드하기 위해 별도의 장치가 필요하다. 대개 JTAG 방식인데 다운로더와 디버거를 겸하고 있다.

5. 다운로더 디버거

가격이 수백만원에 이르는 고가도 있으나 ST-Link나 J-Link처럼 몇만원 짜리도 STM32의 ARM Cortex-M 계열에 사용하기에 충분하다. 저렴한 장치들은 보호회로가 없으니 역삽입하면 회로가 타버리니 주의!

참고: miniJLink (3만 5천원)
http://www.devicemart.co.kr/33262

miniJlink는 Segger 사의 J-Link와 호환되는 다운로더 디버거이다. Segger 사의 홈페이지에서 드라이버와 유틸리티를 다운받아 설치한다.

Segger 홈페이지
https://www.segger.com/

J-Link 유틸리티 설치
https://www.segger.com/jlink-software.html


이제 ARM 개발 환경이 구성되었다. 즐길 차례다!

make 유틸리티 강좌 (4/4)

make 유틸리티 강좌 (4/4)



번  호 : 201
게시자 : 임대영   (RAXIS   )
등록일 : 1995-11-04 05:28
제  목 : [강좌]  make  [4] .. make 마지막.    

---------------------------------------------------------------------
                   make 유틸리티 강좌  - 4 -

                    작성 : RAXIS ( 임대영 )
---------------------------------------------------------------------

목차
        7. Makefile 의 실제 예제.
            7.1 프로그램 제작에 쓰일수 있는 Makefile
            7.2 라이브러리의 제작에 쓰일수 있는 Makefile
            7.3 latex 에서 쓰일수 있는 Makefile

        8. make 수행시에 나타나는 에러들

-------------------------------------------------------------------
7. Makefile 의 실제예제
-------------------------------------------------------------------

지금까지 강좌를 진행하면서 Makefile 의 여러가지 예제들을 제시하였다. 강좌에 나온 예제들을 조금만 바꾸면 자신의 Makefile로써 사용할수 있다. 여기에서는 여러가지 Makefile 들의 기본틀 (template)들을 소개하고자 한다.

7.1 프로그램 제작에 쓰일수 있는 Makefile

여기서는 우선 가장많이 사용되는 C 와 C++ 에서의 Makfile 을 소개하기로 한다. 여러개의 화일들을 컴파일해서 하나의 실행화일을 만드는 예제틀이 바로 [예제7.1]이다.

[에제7.1] ----------------------------------------------------------------

.SUFFIXES = .c .o

CC  = gcc                                                        

INC      =           <-- include 되는 헤더화일의 패스를 추가한다
LIBS     =             <-- 링크시에 필요한 라이브러리를 추가한다.
CFLAGS   = -g $(INC) <-- 컴파일에 필요한 각종옵션을 추가한다.

OBJS     =     <-- 오브젝트 화일의 이름을 적는다.
SRCS     =     <-- 소스화일의 이름을 적는다.

TARGET   =     <-- 링크후에 생성될 실행화일의 이름을 적는다.
  
all      :   $(TARGET)                                           

$(TARGET) : $(OBJS)                                               
      $(CC) -o $@ $(OBJS) $(LIBS)                                 

dep :                                                           
      gccmakedep $(INC) $(SRCS)                                 


clean :                                                           
      rm -rf $(OBJS) $(TARGET) core                               

new   :                                                           
      $(MAKE) clean                                               
      $(MAKE)                                                     

[예제7.1] 에서 바꿔야 할부분은 표시를 해두었다. 자신의 화일들로 적당히 고쳐준 다음 make dep 를 수행시켜본다.  그러면 자동으로 의존관계가 생성된다.

% make dep   <-- 자동으로 의존관계 생성
% make       <-- make 동작

지금까지의 강좌를 이해하고 있다면 위의 Makefile 의 독해란 어렵지 않을 것이다. 개락적인 사항만 설명하기로 한다.

o .SUFFIXES = .c .o

make 내부에서 정의된 접미사 규칙을 이용하기 위한것이다. make는 자동적으로 .c 와 .o 로 끝나는 화일들간에 정의된 규칙이 있는지 찾게 되고 적당한 규칙을 찾아서 수행하게 된다.

o CFLAGS = -g $(INC)

CFLAGS 매크로를 재정의하고 있다. -g 는 디버그 정보를 추가하라는 것이고 $(INC)는 컴파일시에 필요한 include 패스를 적어두는 곳이다.

o all : $(TARGET)

make 는 Makefile 을 순차적으로 읽어서 가장 처음에 나오는 규칙을 수행하게된다. 여기서 all 이란 더미타겟(dummy target)이 바로 첫번째 타겟으로써 작용하게 된다. 관습적으로 all이란 타겟을 정의해 두는것이 좋다. 결과화일이 많을때도 all의 의존관계(dependency)로써 정의해두면 꽤 편리하다.

o dep :
        gccmakedep $(INC) $(SRCS)

의존관계를 자동적으로 생성해주기 위한 것이다. 헤더화일의 패스까지 추가 되어야한다는 것에 주의하기 바람. 이것은 내부적으로 gcc 가 작동되기 때문이다.

[예제7.1] 을 C++ 화일에 이용하기 위해서는 .SUFFIEX , CC, CFLAGS 그리고 타겟에 대한 명령어를 아래의 내용과 같이 바꾸면 된다.

 [예제7.2] -------------------------------------------------------------
.SUFFIXES = .cc .o

 CXX      = g++
 CXXFLAGS = -g $(INC)

 $(TARGET) : $(OBJS)
      $(CXX) -o $@ $(OBJS) $(LIBS)
------------------------------------------------------------------------

물론 각자의 취향에 따라서 Makefile 을 만들어도 된다. 여기서 제시하고 있는 Makefile 은 어디까지나 필자의 관점에서 만든 Makefile 을 소개하고 있는 것 뿐이니까..

7.2 라이브러리와의 링크가 필요한 필요한 Makefile

라이브러리를 만들기위한 Makefile 은 어떤 차이가 있을까. [예제7.1]과 거의 흡사하다. 다만 TARGET 이 실행화일이 아니고 라이브러리라는 것 뿐. 그리고 라이브러리 만드는 방법을 알고 있어야 한다는것.. 참고로 라이브러리 만드는 방법을 소개하기로 한다. read.o , write.o 를 libio.a 로 만들어 보자. 라이브러리를 만들기 위해서는 ar 유틸리티와 ranlib 유틸리티가 필요하다. ( 자세한 설명은 man 을 이용 )

% ar rcv libio.a  read.o write.o
         ^^^^^^^  ^^^^^^^^^^^^^^
a - read.o       <-- 라이브러리에 추가 (add)
a - write.o 

% ranlib libio.a  <-- libio.a 의 인덱스(index)를 생성

그럼 위의 과정을 Makefile 로 일반화시킨다면 어떻게 될까.. 아주 조금만 생각하면 된다. [예제 7.1]에서 TARGET를 처리하는 부분만 아래와 같이 바꾸어 보자.

 [예제7.3]--------------------------------------------------------------------

TARGET = libio.a

 $(TARGET) : $(OBJS)
      $(AR) rcv $@ $(OBJS)  <-- ar rcv libio.a read.o write.o
      ranlib $@             <-- ranlib libio.a

-----------------------------------------------------------------------------

ELF 기반하에서 동적 라이브러리( dynamic library, shared library, 윈도우즈의 DLL)를 만들어 보기로 하자. ELF 상에서는 동적 라이브러리 만드는 방법이 이전에 비해 아주 간단해 졌다. ( 옛날에 모티프 소스 가지고 동적라이브러리 만든다고 고생한것에 비하면 세상이 너무 좋아진것 같음 ) BSD계열의 유닉스를 사용해본 사람이라면 비슷하다는것을 느낄것이다. 그럼 read.c write.c 을 컴파일해서 libio.so.1 을 만들어보자. (so는 shared object 를 의미, 뒤의 .1은 동적 라이브러리의 버전을 의미)

% gcc -fPIC -c read.c    <-- -fPIC 를 추가해서 컴파일한다.
% gcc -fPIC -c write.c

% gcc -shared -Wl,-soname,libio.so.1 -o libio.so.1 read.o write.o
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^
     동적라이브러리를 만들기 위한 옵션

위와 같이 하면 libio.so.1 가 생성된다. 사용자가 만든 동적 라이브러리를 사용하는 방법에 대해서는 간단히만 언급하기로 한다. 우선 libio.so.1 을 /usr/lib 로 옮겨서 ldconfig -v 해서 캐시를 갱신해주던지, 아니면 LD_LIBRARY_PATH 를 지정해두면 된다. 아래는 test.c를 libio.so.1과 링크시키는 예제이다.

% gcc -c test.c
% gcc -o test  test.o -L. -lio  <-- 현재 디렉토리에 있다고 가정
                          ^^^^

[예제7.1] 를 약간 고쳐서 동적라이브러리를 자동적으로 만들어보자. 이번엔 완전한 내용의 Makefile 을 소개한다.

 [에제7.4]------------------------------------------------------------------

.SUFFIXES = .c .o

CC  = gcc

INC      =
LIBS     =
CFLAGS   = -g $(INC) -fPIC    <-- -fPIC 추가

OBJS     =  read.o write.o
SRCS     =  read.c write.c

TARGET   =  libio.so.1        <-- libio.so.1 이 최종화일

all      : $(TARGET)

$(TARGET) : $(OBJS)
      $(CC)  -shared -Wl,-soname,$@ -o $@ $(OBJS)

dep :      gccmakedep $(INC) $(SRCS)

clean :
      rm -rf $(OBJS) $(TARGET) core

-----------------------------------------------------------------------------

아주 조금밖에 바뀌지 않았다. 따라서 이글을 읽는 여러분은 이제 각자의 목적에 맞게 Makefile 을 구성할수 있을것이다. 대부분 접미사 규칙과 최종화일을 생성해내기 위한 명령어가 무엇인지 알고있으면 Makefile 을 자기 개성껏 꾸밀 수 있을것이다.

7.3 latex 에서 쓰일수 있는 Makefile  

Makefile 이 쓰일수 있는 다른 예로써 가장 대표적인 것이 latex 를 사용 할 때이다. 이미 이전 강좌에서 여러차례 소개 된적도 있다. 그럼 doc.tex 를 doc.ps 로 만들어 보기로 하자. 약간 어렵게 하기 위해서 doc.tex 는 내부적 으로 intro.tex 와 conclusion.tex 를 포함하고 있다고 가정한다. ( 논문 같은것을 작성할때는 이렇게 .tex 화일이 많아지게 된다. )

% latex doc.tex      <-- doc.dvi 의 생성
% dvips doc.dvi -o   <-- doc.tex 의 생성

위와 같은 일을 수행하는 Makefile 을 한번 살펴보자.

 [예제7.5]--------------------------------------------------------------------

.SUFFIXES = .tex .dvi   <-- 접미사 규칙

TEX  = latex

OBJ    =  doc.dvi
SRC    =  doc.tex

TARGET =  doc.ps    <-- 결과 화일

all      :   $(TARGET)

$(TARGET) : $(OBJ)
      dvips $(OBJ) -o   <--  dvips doc.dvi -o

new    :                <-- 강제적으로 다시 make
      touch $(SRC) ; $(MAKE)

doc.tex  : intro.tex conclusion.tex   <-- 의존관계 설정

-------------------------------------------------------------------------------

참고로 gccmakedep 는 latex 화일은 지원을 하지 않는것 같다. 따라서 의존관계같은 것은 우리가 직접 적어주어야 한다.

여기에서 제시된 응용말고 그외의 다른 응용에 대해서는 각자가 한번 생각해보기 바란다. 타이핑하기 귀찮은것은 모조리 make 를 이용할수 있다는 것을 염두. 약간은 원인,결과를 따지는 논리(?)가 적용된다고 볼수도 있는 것이 make 라는 것을 기억하기 바람. ( 삼단논법정도만 안다면야..)

8. make 수행시에 나타나는 에러들

make 를 수행하게 되면 이상한 에러에 당황을 하게 되는 경우가 많아, 도대체 어디가 틀렸는지 감을 못잡는 경우가 허다하다. 그런데 make메뉴얼에도 에러에 대한 종류와 그 대처방안에 대해서는 거의 언급이 없는 관계로 이 부분은 필자의 경험에 의거해서 작성한다. ( 에러의 원인,대처방안이 모두 다 틀렸을수도 있다는 것을 염두에 두기 바랍니다. )

에러1)

Makefile:17: *** missing separator.  Stop.

Makefile 을 작성할때 명령어(command)부분은 모두 TAB 문자로 시작해야한다고 강좌[1]부터 강조하였다. 위의 에러는 TAB 문자를 쓰지 않았기 때문에 make 가 명령어인지 아닌지를 구별못하는 경우이다.

대처 1) 17번째줄(근처)에서 명령어가 TAB 문자로 시작하게 바꾼다.

에러2)

make: *** No rule to make target `io.h', needed by `read.o'. Stop.

위의 에러는 의존관계에서 문제가 발생했기 때문이다. 즉 read.c가 io.h에 의존한다고 정의되어 있는데, io.h 를 찾을수없다는 에러이다.

대처2) 의존관계에서 정의된 io.h 가 실제로 존재하는지 조사해본다. 없다면 그 이유를 한번 생각해본다. make dep 를 다시 실행시켜서 의존관계를 다시 생성시켜주는것도 하나의 방법이다.

에러3)

Makefile:10: *** commands commence before first target.  Stop.

위의 에러는 '첫번째 타겟이 나오기 전에 명령어가 시작되었다'는 애매한 에러메시지 이다. 필자가 경험한 이 에러의 원인은 주로 긴 문장을 여러 라인에 표시를 하기 위해서 '\'를 사용할때, 이를 잘못 사용했기 때문인 것 같다. 즉 '\'부분은 라인의 가장 끝문자가 되어야 하는데 실수로 '\'뒤에 스페이스를 몇개 집어넣으면 여지없이 위의 에러가 발생한다.

대처3) 10번째줄(근처)에서 '\'문자가 있거든 이 문자가 라인의 가장 끝 문자가 되도록 한다. 즉 '\'문자 다음에 나오는 글자(스페이스가 대부분)는 모조리 없애버린다.

에러4)

make를 수행시키면 의도했던 실행화일은 안생기고 이상한 행동만 한다. 가령 make clean 했을때와 같은 행동을 보인다.

make는 천재가 아니라는 점을 생각해야 한다. make 는 Makefile 의 내용을읽다가 첫번째 타겟으로 보이는 것을 자신이 생성시켜야 할 결과화일이라고 생각한다. 따라서 clean 부분을 Makefile 의 첫번째 타겟으로 해버리면 위와 같은 결과가 나타나게 된다.

대처4) [예제7.1]에서 all 이라는 필요없는 타겟을 하나 만들어두었다. 이것은 make 가 all 을 첫번째 타겟으로 인식시키기 위함이었다. 따라서 자신이 생성시키고 싶은 결과 화일을 첫번째 타겟이 되게 하던지, 아니면 [에제 7.1]처럼 all과 같은 더미 타겟(dummy target)을 하나 만들어둔다. 그리고 make clean, make dep 같은 부분은 Makefile 의 끝부분에 만들어 두는 것이 안전하다.

에러5)

이미 컴파일했던 화일을 안 고쳤는데도 다시 컴파일한다.

이 행동은 make가 의존관계를 모르기 때문이다. 즉 사용자가 의존관계를 설정해주지않았다는 말이 된다. 따라서 make 는 무조건 모든 화일을 컴파일해서 실행화일을 만드는 일이 자신이 할 일이라고 생가하게 된다.

대처5) 오브젝트화일, 소스화일, 헤더화일들의 의존관계를 설정해주어야 한다. gccmakedep *.c 라고 하면 Makefile 의 뒷부분에 자동적으로 의존관계를 만들어준다. 그외의 다른 화일들에 대해서는 사용자가 적절하게 의존관계를 설정해주어야 한다.

main.o : main.c io.h
read.o : read.c io.h
write.o : write.c io.h

위의 예제는 강좌[1]에서도 제시했던건데.. TARGET : DEPENDENCY의 형식으로 의존관계를 작성한것이다. ( make 에게 의존관계를 알려주는 방법이죠 )

% 그외의 경우에 대해서는 각자가 한번 원인과 결과를 알아보기 바란다. 그리고 팁의 형식으로 글을 올린다면 다른 사람에게도 많은 도움이 될 것이다. 일단 make 에서 에러를 내기 시작하면 초보자는 원인조차 모르는 경우가 많기 때문이다.

─ 강좌를 마치면서 ─────────────────────────
이번 make 강좌는 make 유틸리티에 대한 전반적인 이해와 간단한 Makefile 작성을 목적으로 하였습니다. make 에 관한 모든것을다 소개하지는 않았습니다. ( 개인적인 능력의 한계 !! ) make 를 아주 잘쓰기 위해서는 make 자체에 대한 지식보다는 유닉스(리눅스)의 쉘 프로그램과 고난위도(?)의 명령어까지 알고 있어야 하기 때문이죠. 해커가 될려면 이런 지식을 많이 알고 있어야 합니다. 한가지 아쉬운게 imake 에 대해서 설명을 못한것입니다.  imake 에 대해서는 [make 중급코스]란 이름하에 언젠가 강좌를 해보도록 하죠. ( imake 는 Makefile 을 생성시켜준다고 생각하세요 )

지금 make 다음의 강좌로 뭘 해야 할지 고민중입니다. 우선 X 강좌를 하시는 분(아직은 조용하네요)의 강좌의 이해를 높이기 위해서 X의 개념을 2회 정도의 강좌로 할 생각입니다. X 가 왜 X 이고 어떤 특징을 가지고 있는지 한번 살펴 보기로 하죠.

make 유틸리티 강좌 (3/4)

make 유틸리티 강좌 (3/4)




번  호 : 197
게시자 : 임대영   (RAXIS   )
등록일 : 1995-10-27 02:16
제  목 : [강좌]  make  [3]                    

---------------------------------------------------------------------
                 make 유틸리티 강좌  - 3 -

                 작성 : RAXIS ( 임대영 )
---------------------------------------------------------------------

목차
        4. Makefile 작성시에 알면 좋은것들.
           4.1 긴명령어를 여러라인으로 표시하기.
           4.2 접미사 규칙의 이용 ( Use suffix rule !! )
           4.3 매크로 치환 ( Macro substitution )
           4.4 자동 의존관계 생성 ( Automatic dependency )
      4.5 다중 타겟 ( Multiple target )
           4.6 순환 make ( Recursive MAKE )
           4.7 불필요한 재컴파일 막기.

        5. make 중요 옵션 정리.

        6. Makefile 작성의 가이드라인

---------------------------------------
4. Makefile 작성시에 알면 좋은것들.
---------------------------------------

Makefile을 작성할때 기본적으로 알고 있으면 유익한 것들을 기술한다. 이전 강좌의 내용을 대체로 이해하고 있다면 좋은 팁이 될것이다. 메뉴얼에 나오는 광범위한 내용은 다루지 않고 기본적인 것들에 관심을 두기로 한다

4.1 긴명령어를 여러라인으로 표시하기.

Makefile을 작성중애 명령어가 한줄을 넘어 간다고 가정하자. 이때 그냥 줄줄 적는다면 읽기도 힘들고, 작성하는 사람도 조금 찜찜하다. 이때 '\' 문자를 이용해서 여러라인으로 나타낼수 있다. 이미 C언어에 익숙한 사람이라면 낯익은 기호일것이다. 아래의 예제를 보자.

[예제 13].............................................................

OBJS =  shape.o \
        rectangle.o \
        circle.o \
        line.o \
        bezier.o 
......................................................................


위의 예제는 OBJS = shape.o rectangle.o circle.o line.o bezier.o 라는 문장을 여러라인으로 표시한것이다. 보기에도 깔끔해 보이지 않은가.

4.2 접미사 규칙의 이용 ( Use suffix rule !! )

강좌 2 에서 접미사 규칙에 대해서 많이 설명을 했다. Makefile의 작성시 C, C++, tex 등의 화일은 이미 정의되어 있는 규칙을 이용하면 간단하고, 깔끔한 Makefile을 작성할수 있다. 강좌2에서 직접 우리가 규칙을 간단히 구현해보기도 했는데, 이것은 접미사 규칙의 개념을 설명하기 위함이었다.

어떤 화일들이 이미 규칙으로 정해져 있는지 한번 살펴보기로 한다. 아래에 열거된 화일들은 특별히 따로 정의하지 않은 상테에서 바로 이용할 수 있는 것들이다. ( GNU Make 메뉴얼에 바탕을 두고 작성되었다. )

   C 컴파일             ( XX.c -> XX.o )
   C++ 컴파일           ( XX.cc 또는 XX.C --> XX.o )
   Pascal 컴파일        ( XX.p --> XX.o )
   Fortran 컴파일       ( XX.f 또는 XX.F --> XX.o )
   Modula-2 컴파일      ( XX.def --> XX.sym )  
                       ( XX.mod --> XX.o )
   assembly 컴파일      ( XX.s --> XX.o )
   assembly 전처리      ( XX.S --> XX.s )
   single object file의 링크 ( XX.o --> XX )
   Yacc 컴파일(?)       ( XX.y --> XX.o )
   Lex 컴파일(?)        ( XX.l --> XX.o )
   lint 라이브러리 생성  ( XX.c --> XX.ln )
   tex 화일 처리        ( XX.tex --> XX.dvi )
   texinfo 화일처리     ( XX.texinfo 또는 XX.texi --> XX.dvi )
   RCS 화일 처리        ( RCS/XX,v --> XX )
   SCCS 화일처리        ( SCCS/XX.n --> XX )

위에 정의된 화일만이 make에서 처리할수 있는것은 아니다. 그밖의 화일에 대해서는 사용자가 직접 정의해주면 얼마든지 make를 사용할수 있다. 그럼 이젠 위와 같은 화일들을 처리하기 위한 명령어는 어떤 매크로로 정의되어 있는지 알아보자. 이미 말했듯이 아래에 열거된 매크로는 재정의 가능하다. 가령 TEX = tex 이지만 대부분 TEX = latex 로 재정의되어야 할 것이다.

AR = ar ( Archive maintaining program )
AS = as ( Assembler )
CC = cc ( = gcc , C compiler )
CXX = g++ ( C++ compiler )
CO = co ( extracting file from RCS )
CPP = $(CC) -E ( C preprocessor )
FC = f77 ( Fortran compiler )
LEX = lex ( LEX processor )
PC  = pc ( Pascal compiler )
YACC = yacc ( YACC processor )
TEX = tex ( TEX processor )
TEXI2DVI = texi2dvi ( Texiinfo file processor )
WEAVE = weave ( Web file processor )
RM  = rm -f ( remove file )

이미 강좌2에서 밝혔지만 위의 명령어에서 사용될 FLAG(옵션) 에 정의한 매크로에 대해서도 알아보기로 한다.

* ARFLAGS =     (ar achiver의 플래그 ) 
  ASFLAGS =     (as 어셈블러의 플래그 )
* CFLAGS = (C 컴파일러의 플래그 )
* CXXFLAGS =    (C++ 컴파일러의 플래그 ) 
  COFLAGS =     (co 유틸리티의 플래그 )
  CPPFLAGS =    (C 전처리기의 플래그 )
  FFLAGS =      (Fortran 컴파일러의 플래그 )
* LDFLAGS =     (ld 링커의 플래그 ) 
* LFLAGS =      (lex 의 플래그 )
  PFLAGS =      (Pascal 컴파일러의 플래그 )
* YFLAGS =      (yacc 의 플래그 )

위애서 '*"표시를 한것은 자주쓰이게 될 플래그들을 표시한것이다. 위에서 표시한 여러가지 매크로들을 재정의를 무조건 하라는 배려에서인지 대부분 값이 설정되어 있지 않다. 가령 C프로그램을 짤때 CFLAGS 를 재정의해야 할 것이다

4.3 매크로 치환 ( Macro substitution )

매크로를 지정하고, 그것을 이용하는것을 이미 알고 있다. 그런데 필요에
의해 이미 매크로의 내용을 조그만 바꾸어야 할 때가 있다. 매크로 내용의
일부만 바꾸기 위해서는 $(MACRO_NAME:OLD=NEW)와 같은 형식을 이용하면
된다.

MY_NAME   = Michael Jackson
YOUR_NAME = $(NAME:Jack=Jook)

위의 예제에서는 Jack 이란 부분이 Jook 으로 바뀌게 된다. 즉 YOUR_NAME이란 매크로의 값은 Michael Jookson 이 된다. 아래의 예제를 하나 더
보기로 한다.               ^^^^

OBJS = main.o read.o write.o
SRCS = $(OBJS:.o=.c)

위의 예제에서는 OBJS 에서 .c가 .o로 바뀌게 된다. 즉 아래와 같다.

SRCS = main.c read.c write.c 

위의 예제는 실제로 사용하면 아주 편할때가 많다. 가령 .o 화일 100개에 .c 화일이 각각 있을때 이들을 다 적을려면 무척이나 짜증나는 일이 될 것이다.

4.4 자동 의존관계 생성 ( Automatic dependency )

일반적인 make의 구조는 아래와 같이 target, dependency,command가 연쇄적으로 정의되어 있는 것과 같다고 하였다.

target : dependency 
        command ....

그런데 위에서 command가 없이 타겟과 의존관계만 표시가 되면 이는 타겟이 어느 화일에 의존하고 있는지 표시해주는 정보의 역할을 한다. 이런 정보는 Makefile 을 작성할 때 없어서는 안되는 부분이다. (이 부분이 없으면, make 는 증말 바보처럼 행동합니다. ) 그런데 일일이 이런 정보를 만든다는것은 쉬운 일이 아니다. 화일이 1000 개라고 할때 이것을 어케 다 표시하누.. 이런 단조롭고 귀찮은 일을 자동으로 해주는 좋은 유틸리티가 있다. 우선gccmakedep가 있는지 확인해 보자. gccmakedep 는 어떤 화일의 의존관계를 자동으로 조사해서 Makefile의 뒷부분에 자동으로 붙여주는 유틸리티이다. gccmakedep가 없다면 gcc -M XX.c 라고 해보자. 그러면 XX.c의 의존관계가 화면에 출력됨을 알수 있을것이다. ( gccmakedep 도 내부적으로 gcc -M 을 사용한다. )

프로그램 인스톨할때 make dep 라는 것을 친 기억이 있을것이다. 화일들의 의존관계를 작성해준다는 의미이다. 그럼 우리의 Makefile에도 이런 기능을 첨가해보기로 한다.

[예제 14].............................................................

.SUFFIXES = .c .o 
CFLAGS = -O2 -g

OBJS = main.o read.o write.o 
SRCS = $(OBJS:.o=.c)

test : $(OBJS)
        $(CC) -o test $(OBJS)

dep :
        gccmakedep $(SRCS)
......................................................................

위의 Makefile을 이해할수 있다면 이제 Makefile에 대해서 어느정도 도가 텃다고 해도 무방할것이다. 위의 예제에서 화일들간의 의존관계가 없다. 그럼 이제 make dep 을 써서 자동적으로 생성시켜보자.

% make dep 
% vi(emacs) Makefile

Makefile 의 뒷부분에 다음과 같은 내용이 붙어있는것을 알게 될것이다.

[예제 15].............................................................

# DO NOT DELETE
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
 /usr/include/sys/cdefs.h /usr/include/libio.h \
 /usr/include/_G_config.h io.h
read.o: read.c io.h
write.o: write.c io.h
......................................................................

main.o 에는 조금 자질구래한 헤더화일까지 붙어있다. 이것은 헤더화일 안에서 include 하는 화일들을 다 찾다보니까 그런것이다. 별로 신경쓸 것은 없고..  대충 우리가 지금까지 손으로 작성해온 것과 거의 흡사함을 알 수 있다. 아니 오히려 더 정확함을 알수 있다. ( 이제부터 Make 는 스마트하게 동작한다. )

4.5 다중 타겟 ( Multiple target )

하나의 Makefile 에서 꼭 하나의 결과만 만들어 내라는 법은 없다. 가령 결과 화일이 3개가 필요하다고 하자. 아래의 예제를 보기로 한다.

[예제 15].............................................................

.SUFFIXES = .c .o 
CC     = gcc
CFLAGS = -O2 -g

OBJS1 = main.o test1.o   <-- 각각의 매크로를 정의
OBJS2 = main.o test2.o 
OBJS3 = main.o test3.o 
SRCS = $(OBJS1:.o=.c) $(OBJS2:.o=.c) $(OBJS3:.o=.c)   

all : test1 test2 test3  <-- 요기에 주의 

test1 : $(OBJS1)
        $(CC) -o test1 $(OBJS1) 

test1 : $(OBJS2)
        $(CC) -o test2 $(OBJS2)

test1 : $(OBJS3)
        $(CC) -o test3 $(OBJS3)

dep :
        gccmakedep $(SRCS)
......................................................................

위의 프로그램은 make all 을 함으로써 동작한다. 실제로 동작시켜보면 아래 와 같은 결과가 나온다.

% make all ( 또는 make )
gcc -O2 -g   -c  main.c -o main.o
gcc -O2 -g   -c  test1.c -o test1.o
gcc -o test1 main.o test1.o             <-- test1 의 생성
gcc -O2 -g   -c  test2.c -o test2.o
gcc -o test2 main.o test2.o             <-- test2 의 생성
gcc -O2 -g   -c  test3.c -o test3.o
gcc -o test3 main.o test3.o             <-- test3 의 생성

4.6 순환 make ( Recursive MAKE )

규모가 큰(?) 프로그램들은 화일들이 하나의 디렉토리에 있지 않는 경우가 많다. 여러개의 서브시스템이 전체 시스템을 구성한다고 가정하면 각 서브 시스템에 Makefile이 존재한다. (서브시스템 = 서브디렉토리 ) 따라서 여러개의 Makefile을 동작시킬 필요가 있도록 Makefile 을 고쳐보자. 서브 디렉토리에 있는 Makefile을  동작시키는 방법은 의외로 간단하다. 아래의 간단한 예제를 보자.

[예제 16].............................................................

subsystem:
        cd subdir; $(MAKE)      .....(1)

subsystem:
        $(MAKE) -C subdir       .....(2)
......................................................................


위의 예제에서 (1)과 (2)는 동일한 명령을 수행한다 (1)을 기존으로 동작을 한번 묘사해보자. 우리가 만들 시스템의 타겟이 subsystem이다. (이름은 아무래도 상관없다) 우선 subdir이라는 곳으로 가서, 거기에 있는 Makefile 을 동작시키게 된다. ( 간단하죠 ) MAKE라는 것은 그냥 make 라는 명령어를 표시하는 매크로일뿐..  그럼 완전한 예제를 한번 구성해 보기로 한다.

[예제 16].............................................................

.SUFFIXES = .c .o
CC     = gcc
CFLAGS = -O2 -g

all :  DataBase Test      <-- 요기에 집중.

DataBase:
        cd db ; $(MAKE)   <-- db 로 이동해서 make 실행

Test: 
        cd test ; $(Make) <-- db 로 이동해서 make 실행
......................................................................

위의 예제에서 db, test 디렉토리에 있는 Makefile은 지금까지 우리가 공부했던 Makefile 과 거의 흡사하다고 가정하자. 그럼 위의 Makefile 을 실행시켜본다.

% make 
cd db ; make
make[1]: Entering directory`/home/raxis/TEST/src'
gcc -O2 -g   -c DBopen.c -o DBopen.o
gcc -O2 -g   -c DBread.c -o DBread.o
gcc -O2 -g   -c DBwrite.c -o DBwrite.o
make[1]: Leaving directory `/home/windows/TEST/src'
cd test ; make
make[1]: Entering directory `/home/raxis/TEST/test'
gcc -O2 -g   -c test.c -o test.o
make[1]: Leaving directory `/home/windows/TEST/test'

위의 가상 실행을 보면 우선 db 로 가서 거기의 Makefile을 수행시키고, 다음에는 test로 가서 Makefile 을 실행시킴을 볼수 있다. 우선은 단순하게 컴파일만 시켰는데, 다르게 한번 생각해보자. db 디렉토리에서의 최종 타겟으로 가령 db.a을 만들어 내고 test 디렉토리에서 이를 링크시킨다고 생각하면 꽤 괜찮은 시나리오가 될것이다. 위에서 [1]이라고 나타난것은 현재의 레벨을 의미한다. 원래 디렉토리의 레벨이 [0]이고, 여기서는 한레벨 더 내려갔으므로 [1]이라고 표시된 것이다

4.7 불필요한 재컴파일 막기.

의존관계 규칙에 의해 하나가 바뀌면 그에 영항받는 모든 화일이 바뀐다고 앞에서 말했다. 그러나 다른 화일들에게 아무 영향을 주지않을 수정이 있었는 데도 재컴파일을 시도한다면 시간낭비가 될수도 있다. 가령 모든 .c 화일에서 include 하는 헤더화일에서 새로운 #define PI 3.14 라고 정의를 했다고 가정하자. 그리고 PI 라는것은 아무곳에서도 사용을 하지 않는다.

이때는 'make -t' 라고 해보자. -t 는 touch를 의미하는 옵션으로써 컴파일을 하지않는 대신 화일의 생성날짜만 가장 최근으로 바꾸어 놓는다. 새로 컴파일 된것처럼 처리를 하는 것이다. touch유틸리티 명렁어에 익숙한 사람이라면 이해할 것이다. touch는 화일의 생성날짜를 현재로 바꾸어 주는 간단한 유틸리티이다.

---------------------------------------
5. make 중요 옵션 정리.
---------------------------------------

make에서 거의 모든것은 Makefile내부에서 모두 지정을 할수 있다. 그 중 일부를 make의 실행시에 옵션으로 통해서 줄수도 있다. 수많은 옵션중에서 기억해둘만한 것 몇가지만 소개하기로 한다.

  -C dir  ... 위에서도 밝혔듯이 Makefile 을 계속 읽지말고 우선은 
              dir 로 이동하라는 것이다. 순환 make 에 사용된다.

  -d      ... Makefile 을 수행하면서 각종 정보를 모조리 출력해준다. 
( --debug )   출력량이 장난이 아님.. 결과를 화일로 저장해서 읽어보면 
              make 의 동작을 대충 이해할수 있다.

  -h      ... 옵션에 관한 도움말을 출력한다. 
( --help )

  -f file ... file 에 해당하는 화일을 Makefile 로써 취급한다.  
( --file )

  -r      ... 내장하고 있는 각종 규칙(Suffix rule 등) 을 없는것으로 
( --no-builtin-rules )  간주한다. 따라서 사용자가 규칙을 새롭게 정의해
                        주어야 한다.

  -t      ... 화일의 생성날짜를 현재시간으로 갱신한다. 
( --touch )

  -v      ... make 의 버전을 출력한다. ( 전 GNU make 3.73 을 씁니다.) 
( --version )

  -p      ... make 에서 내부적으로 세팅되어 있는 값들을 출력한다. 
( --print-data-base )

  -k      ... make 는 에러가 발생하면 도중에 실행을 포기하게 되는데 
( --keep-going ) -k 는 에러가 나더라도 멈추지 말고 계속 진행하라는 뜻

---------------------------------------
6. Makefile 작성의 가이드라인
---------------------------------------

make를 많이 써본 사람은 어느정도 자신만의 Makefile을 작성하는 일정한 스타일 같은것이 있다. 프로그램이 짜는 사람마다 다르듯이 Makefile 도 각각이다. 여기서는 그냥 가장 일반적인 가이드라인을 제시하기로 한다. 다음 강좌에서 Makefile 의 여러 예제를 살펴보면서 다시한번 자세히 설명
할 것이다.

  o  매크로를 잘 사용하면 Makefile이 깔끔해질뿐 아니라, 내용의 수정도
     용이하다. 조금 과장해서 말한다면, 최대한 매크로를 많이 사용하라고
     말하고 싶다. Makefile내에서 두번이상 나오는것들은 매크로로 정의해
     두면 편하다. 자신의 프로그램 특성에 따라서 기존의 매크로를 재정의
     하는것도 좋다.

  o  make 에서 정의되어 있는 규칙들을 최대한 이용한다. 접미사 규칙은
     무조건 이용하기를 권한다. 기존의 규칙들을 자기가 정의하는것도
     좋지만, 억지로 이럴 필요는 없다.

  o  대체로 아래와 같이 Makefile 을 구성한다.
         o  매크로 정의 부분
         o  타겟을 얻기위한 명령어 부분
         o  의존관계 부분

     지금까지 예제로 나온 것들이 모두 위의 순서대로 작성되어 있다.
     반복이 되겠지만 다시한번 예제를 제시해본다.

[예제 17].............................................................

.SUFFIXES = .c .o                     ----+
CFLAGS    = -g                            |
                                          |
OBJS      = main.o \                      |
            read.o \                      |
            write.o                       | 매크로 정의 부분
SRCS      = $(OBJS:.o=.c)                 |
                                          |
TARGET    = test                      ----+

$(TARGET): $(OBJS)                   ----+
        $(CC) -o $@ $(OBJS)               |
dep  :                                    |
        gccmakedpend $(SRCS)              | 
new  :                                    | 명령어 정의 부분
        touch $(SRCS) ; $(MAKE)           |
clean :                                   |
        $(RM) $(OBJS) $(TARGET) core  ----+

                                      ---- 여기부터 의존관계 부분
......................................................................

위의 예제는 최대한 매크로를 많이 이용할려고 했기 때문에 독해(?)하기 어려울 수도 있다.

----다음편 예고------------------------------------------------------
다음편에는 Makefile 의 여러가지 작성 예제를 가지고 설명을 하겠습니다. 아직 make에 익숙하기 않을것이라고 생각하기 때문입니다. 최대한 다양한 예제를 다룸으로써 나중에 많은 도움이 되도록 하죠. make 수행중에 나올 수 있는 여러 예러들도 다음강좌에서 한번 다루어 보죠. 그리고 가능하다면 Imakefile란 것도 한번 건드려 봅시다.



make 유틸리티 강좌 (2/4)

make 유틸리티 강좌 (2/4)



번  호 : 195
게시자 : 임대영   (RAXIS   )
등록일 : 1995-10-25 03:25
제  목 : [강좌]  make  [2]                    
---------------------------------------------------------------------
                    make 유틸리티 강좌  - 2 -

                        작성 : RAXIS ( 임대영 )
---------------------------------------------------------------------

2번째 강좌가 늦어진 점 죄송하게 생각합니다. 오늘의 강좌내용입니다.

목차
        3. 매크로( Macro ) 와 Suffix 규칙
             3.1 매크로의 정의 ( What is macro )
             3.2 미리정해져이는 매크로 ( Pre-defined macro )
             3.3 확장자 규칙 ( Suffix rule )
             3.4 내부 매크로 ( Internal macro )

-------------------------------------------------------------------
3. 매크로( Macro ) 와 Suffix 규칙
-------------------------------------------------------------------

******************************************
  3.1 매크로의 정의( What is Macro )
******************************************

앞 강좌에서 매크로에 대해서 대충 언급을 했다. 프로그램을 짜본 사람이나 로터스,한글,엑셀등등의 모든 패키지에서 매크로라는 것을 사용하게 된다. 은연중에 매크로의 정의는 대충 짐작하고 있을것이다. 이미 알고 있는바와 같이 매크로는 특정한 코드를 간단화시켜 포현한것에 지나지 않는다. Makefile 에서 사용되는 매크로는 비교적 그 사용법이 간단하기 때문에 금방 익혀서 사용할 정도가 된다.

매크로의 정의는 프로그램을 작성할때 변수를 지정하는 것처럼 하면 된다. 그리고 매크로를 사용하기 위해서는 $(..) 을 이용하면 된다. 아래는 매크로의 간단한 예제이다.

[ 참고 - 매크로의 사용에서 ${..} $(..) $.. 를 모두 사용할수 있습니다.
[        그러나 대부분의 책에서는 $(..) 을 사용하라는 군요.

----Makefile [예제 4]--------------------------

OBJS = main.o read.o write.o

test : $(OBJS)                   <------(1)
        gcc -o test $(OBJS)

     ..........
----------------------------------------------

강좌 1에서 다루었던 예제와 거의 비슷하다. 매크로는 사실상 복잡한것을 간단하게 표시한것에 지나지 않는다. (1) 번을 매크로를 안쓰고 표현한다면 아마 아래와 같이 될것이다.

----Makefile [예제 5]--------------------------

test : main.o read.o write.o
        gcc -o test  main.o read.o write.o

----------------------------------------------

[ 참고 - [예제 5]가 더 쉽지 않느냐고 반문하는 사람은 매크로의 위력을
[        잘 모르는 사람입니다. 거의 모든 소프트웨어에서 매크로를 지원
[        하는 이유를 한번 잘 생각해봅시다. [예제 4] 의 (1) 부분이 이해
[        하기 난해하다고 하실지는 모르겠지만, 대충 형식이 정해져 있기
[        때문에 조금만 익숙해 지면 오히려 더 편할겁니다.

make 에 관해 설명한 책에 다음과 같은 명언(?) 이 나온다.

    Macro makes Makfile happy. ( 매크로는 Makefile 을 기쁘게 만든다.)

이 말은 Makefile을 작성함에 있어 매크로를 잘만 이용하면 복잡한 작업도 아주 간단하게 작성할수 있음을 말해주는 말이 아닐까 생각한다. 매크로에 대해서는 더이상 말할것이 없다. ( 너무 간단하죠 ? ) 이제 남은것은 여러분들이 자신의 매크로를 어떻게 구성하느냐이다. 어떤것을 매크로로 정의해햐 할지는 여러분들의 자유이며, 나중에 전반적인 지침을 설명할 것이다.

***************************************************
3.2 미리 정해져있는 매크로 ( Pre-defined macro )
***************************************************

여러분들보다 머리가 약간 더 좋은 사람들이 make 라는것을 만들면서 미리 정해놓은 매크로들이 있다. 'make -p' 라고 타이핑해보면 make 에서 미리 세팅되어 있던 모든 값들( 매크로, 화경변수(environment)등등)이 엄청 스크롤 된다. 이값들을 보고 미리 주눅이 들 필요는 없다. 어차피 대부분의 내용들은 우리가 재정의 해주어야 하기 때문에 결론적으로 말하면 우리가 모두 작성한다고 생각하는것이 맘 편하다.

아래에는 대부분이 UNIX 계열의 make 에서 미리 정해져 있는 매크로들중 몇가지만 나열해본 것이다.

---- Predefined Macro [예제 6] --------------------------

ASFLAGS =                    <-- as 명령어의 옵션 세팅
AS      = as
CFLAGS  =                    <-- gcc 의 옵션 세팅
CC      = cc (= gcc )
CPPFLAGS =                   <-- g++ 의 옵션
CXX     = g++
LDLFAGS =                    <-- ld 의 옵션 세팅
LD      = ld
LFLAGS  =                    <-- lex 의 옵션 세팅
LEX     = lex
YFLAGS  =                    <-- yacc 의 옵션 세팅
YACC    = yacc
MAKE_COMMAND    = make

--------------------------------------------------------

[ 참고 - 직접 make -p 를 해서 한번 확인해보세요. 과연 make 는 내부적
[        으로 어떤 변수들을 사용하고 있는지 알아봅시다. 매크로는 관습
[        적으로 대분자로 작성되니까 이점에 유의해서 보세요.
[        make는 쉘상에서 정의한 환경변수값들을 그대로 이용한다는 것을
[        알고 계시기 바랍니다.

위에 열거한 매크로는 make 에서 정의된 매크로중 그야말로 일부에 지나지 않는다. 하지만 프로그램을 작상함에 있어 가장 많이 사용하게될 매크로 들이다. 이들 매크로는 사용자에 의해 재정의 가능하다. 가령 gcc 의 옵션 중에 디버그 정보를 표시하는 '-g' 옵션을 넣고 싶다면, 아래와 같이 재정의 한다.

   CFLAGS = -g

[예제 6] 의 각종 FLAG 매크로 들은 대부분 우리가 필요에 의해 세팅해 주어야 하는 값들이다. 왜 굳이 make 에서 값도 정의되지 않은 매크로를 우리가 정의 해서 써야 하는지 의문을 던질지도 모른다. 우리가 더 이쁜 이름으로 매크로를 정의할수도 있다고 하면서....

여기서 한가지 사실을 생각해봐야 할것이다. make에서 위에 나온 것들을 왜 미리 정해두었을까 ? ( 왜일까요? ) make 에서 이들 매크로를 제공하고 있는 이유는 내부적으로 이들 매크로를 사용하게 되기 때문이다. 어떻게 이용하는지는 확장자 규칙(Surfix rule)을 설명하면서 해답을 제공할 것이다.  이제 [예제 4] 의 Makefile을 매크로를 이용하여 깔끔하게(?) 작성해보자.

----Makefile [예제 7]--------------------------

OBJECTS = main.o read.o write.o
SRCS    = main.c read.c write.c  <-- 없어도 됨

CC      = gcc       <-- gcc 로 세팅
CFLAGS  = -g -c     <-- gcc 의 옵션에 -g 추가

TARGET  = test      <--- 결과화일을 test 라고 지정

$(TARGET) : $(OBJECTS)
        $(CC) -o $(TARGET) $(OBJECTS)

clean  :
        rm -rf $(OBJECTS) $(TARGET) core

main.o : io.h main.c       <-- (1)
read.o : io.h read.c
write.o: io.h write.c
----------------------------------------------

위의 Makfile 을 동작시켜보자.

% make
gcc -g -c main.c -o main.o
gcc -g -c read.c -o read.o
gcc -g -c write.c -o write.o
gcc -o test main.o read.o write.o  <- OK

% make clean
rm -rf main.o read.o write.o test core  <- OK

그런데 여기서 한가지 이상한 점을 발견하게 될것이다. .c 화일을 .o 화일로 바꾸는 부분이 없는데 어떻게 컴파일이 되었을까? 빼먹고 타이핑 못한것은 아닐까하고 ...  절대아님 !

앞에서 CFLAGS 같은 매크로는 make 화일의 내부에서 이용된다고 하였다. 그렇다면 make 는 과연 어디에서 이용을 할까?  바로 컴파일 하는곳에서 이용을 하는것이다. 따라서 우리는 CFLAGS 를 세팅해주기만 하면 make 가 알아서 컴파일을 수행하는 것이다. ( 얼마나 편리합니까.)

[ 참고 - 확장자 규칙 에서 다시 한번 자세히 설명을 하겠습니다.

(1) 에 해당하는 부분은 어떤 화일이 어디에 의존하고 있는지를 표시해 주기 위해서 꼭 필요하다. .c 화일을 컴파일하는 부분은 일괄적인 루틴으로 작성할수 있기 때문에 이들 화일간의 의존관계(dependency)를 따로 표시해주어야 한다.

[ 참고 - 화일간의 의존관계를 자동으로 작성해주는 유틸리티가 있습니다.
[        이것은 다음 번 강좌에서 다루기로 합니다.

***********************************
  3.3 확장자 규칙 ( Suffix rule )
***********************************

확장자 규칙이란 간단히 말해서 화일의 확장자를 보고, 그에 따라 적절한 연산을 수행시키는 규칙이라고 말할수 있다. 가령 .c 화일은 일반적으로 C 소스코드를 가르키며, .o 화일은 목적화일(Object file)을 말하고있다. 그리고 당연히 .c 화일은 컴파일 되어서 .o 화일이 되어야 하는것이다.

여기서 한가지 매크로가 등장하게 된다. .SUFFIXES 라고 하는 매크로인데 우리가 make 화일에게 주의깊게 처리할 화일들의 확장자를 등록해 준다고 이해하면 될것이다.

.SUFFIXES = .c .o

위의 표현은 .c 와 .o 확장자를 가진 화일들을 확장자 규칙에 의거해서 처리될수 있도록 해준다. .SUFFIXES 매크로를 이용한 예제를 살펴보자.

----Makefile [예제 8]--------------------------

.SUFFIXES = .c .o

OBJECTS = main.o read.o write.o
SRCS    = main.c read.c write.c

CC      = gcc    
CFLAGS  = -g -c

TARGET  = test

$(TARGET) : $(OBJECTS)
        $(CC) -o $(TARGET) $(OBJECTS)

clean  :
        rm -rf $(OBJECTS) $(TARGET) core

main.o : io.h main.c    
read.o : io.h read.c
write.o: io.h write.c
----------------------------------------------

위의 Makfile 을 동작시켜보자.

% make
gcc -g -c main.c -o main.o
gcc -g -c read.c -o read.o
gcc -g -c write.c -o write.o
gcc -o test main.o read.o write.o  <- OK

확장자 규칙에 의해서 make 는 화일들간의 확장자를 자동으로 인식해서 필요한 작업을 수행한다. 즉 아래의 루틴이 자동적으로 동작을 하게된다.

.c.o :
        $(CC) $(CFLAGS) -c $< -o $@

[ 참고 -  gmake 에서는 약간 다르게 정의 되어 있지만, 우선은 같다고
[         이해합시다. $< , $@ 에 대해서는 곧 설명합니다.

우리가 .SUFFIXES = .c .o 라고 했기 때문에 make 내부에서는 미리 정의된 .c( C 소스 )를 컴파일해서 .o(목적화일)을 만들어내는 루틴이 자동적으로 동작하게 되어 있다.  CC 와 CFLAGS 도 우리가 정의한 대로 치환될 것임은 의심할 여지가 없다.

make 내부에서 기본적으로 서비스를 제공해주는 확장자들의 리스트를 열거해보면 아래와 같다. 각 확장자에 따른 자세한 설명은 생략한다.

.out .a .ln .o .c .cc .C .p .f .F .r .y .l .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo .w .ch .web .sh .elc .elf

Makefile 내부에서 .SUFFIXES 매크로의 값을 세팅해주면 내부적으로 정의된 확장자의 연산이 동작을 하게 된다. 따라서 확장자 규칙은 make 가 어느 확장자를 가진 화일들을 처리할것인가를 정해주는 것이라고 생각할 수 있다.

그러나 이것은 필자만의 생각이지만... make 에서 자동적으로 확장자를 알아서 해주는 것이 좋긴 하지만, 필자는 일부러 위의 .c.o 에 해당되는 부분을 그냥 정의해서 쓰길 더 좋아한다. 이것은 지금까지의 습관상 그렇지만 왠지 우리가 정의하는것이 더 자유롭게(flexible) 사용할수 있을것 같기 때문이다. 그리고 이런 기능은 우리가 작성을 해봐야 make의 메카니즘을 더 잘 이해할수 있다고 생각한다.

[예제 8] 의 내용을 약간 바꾸어보자.

----Makefile [예제 9]--------------------------

.SUFFIXES = .c .o

OBJECTS = main.o read.o write.o
SRCS    = main.c read.c write.c

CC      = gcc    
CFLAGS  = -g -c
INC     = -I/home/raxis/include  <-- include 패스 추가

TARGET  = test

$(TARGET) : $(OBJECTS)
        $(CC) -o $(TARGET) $(OBJECTS)

.c.o :                          <-- 우리가 확장자 규칙을 구현
        $(CC) $(INC) $(CFLAGS) $<

clean  :
        rm -rf $(OBJECTS) $(TARGET) core

main.o : io.h main.c
read.o : io.h read.c
write.o : io.h write.c
----------------------------------------------

% make
gcc -I/home/raxis/include -g -c main.c
gcc -I/home/raxis/include -g -c read.c
gcc -I/home/raxis/include -g -c write.c
gcc -o test main.o read.o write.o  <-- OK

[예제 8] 과 [예제 9] 의 차이는 그저 .c.o 부분을 누가 처리하느냐이다. 그리고 [예제 9]에서는 INC 라는 매크로를 추가시켜서 컴파일시에 이용하도록 하였다.

***************************************
  3.4 내부 매크로 ( Internal macro )
***************************************

make 에서는 내부 매크로라는 것이 있다. 이것은 우리가 맘대로 정할수 있는 매크로는 절대 아니다. 대신 매크로를 연산, 처리하는데 쓰이는 매크로 라고 하는 것이 더 적당할 것이다.

---- Internal Macro  [예제 10]--------------------

$*  <-- 확장자가 없는 현재의 목표화일(Target)

$@  <-- 현재의 목표화일(Target)

$<  <--  현재의 목표화일(Target)보다 더 최근에 갱신된 화일이름

$?  <--  현재의 목표화일(Target)보다 더 최근에 갱신된 화일이름
-------------------------------------------------

[ 참고 -- 책에서는 $< 와 $? 를 약간 구분하고 있지만 거의 같다고 봐도
[         무방할 것입니다.

각 내부 매크로에 대한 예를 보기로 한다.

main.o : main.c io.h
        gcc -c $*.c

$* 는 확장자가 없는 현재의 목표화일이므로 $* 는 결국 main 에 해당한다.

test : $(OBJS)
        gcc -o  $@  $*.c

$@ 는 현재의 목표화일이다. 즉 test 에 해당된다.

.c.o :
        gcc -c $<      ( 또는 gcc -c $*.c )

$< 는 현재의 목표화일보다 더 최근에 갱신된 화일이름이라고 하였다. .o 화일보다 더 최근에 갱신된 .c 화일은 자동적으로 컴파일이 된다. 가령 main.o 를 만들고 난 다음에 main.c 를 갱신하게 되면 main.c는 $< 의 작용에 의해 새롭게 컴파일이 된다.

[ 참고 - 이제 [예제 9] 을 이해할수 있겠습니까.?

[ 참고 - Makfile 화일을 작성해놓고, 그냥 make 만 치시면 make 는
[        Makefile 의 네용을 살펴보다가 첫번째 목표화일에 해당되는
[        것을 실행시키게 됩니다. 따라서 위의 예제에서는 make test
[        라고 해도 같은 결과를 내게 됩니다. 반면 clean 에 해당하는
[        부분을 윗부분에 두게되면 make 는 항상 make clean 을 수행
[        하게 됩니다.
[
[       % make  <-- make clean 이 실행됨
[        rm -rf main.o read.o write.o test core
[    
[       % make test <-- 강제적으로 test 가 생성되게 한다.
[       gcc -I/home/raxis/include -g -c main.c
[       gcc -I/home/raxis/include -g -c read.c
[       gcc -I/home/raxis/include -g -c write.c
[       gcc -o test write.c main.o read.o write.o  <-- OK


Makefile 의 이해를 돕기 위해서 Makefile 을 하나더 작성해보기로 한다. make.tex 화일을 make.dvi 로 만든다음 이것을 다시 make.ps 로 만드는 것이다. 보통의 순서라면 아래와 같다.

% latex make.tex    <-- make.dvi 가 만들어진다.

% dvips make.dvi -o  <-- make.ps 가 만들어진다.

보통의 가장 간단한 Makefile 을 작성해보면 아래와 같다.

---- Makefile [예제 11]--------------------

make.ps : make.dvi
     dvips make.dvi -o

make.dvi : make.tex
        latex make.tex
------------------------------------------

위와 같은 일을 하는 Makefile 을 다르게 한번 작성해보자. 매크로를 어느정도 사용해보기로 하며, 확장자 규칙을 한번 적용해보기로 한다.

---- Makefile [예제 12]--------------------

.SUFFIXES = .tex .dvi

TEX       = latex         <-- TEX 매크로를 재정의

PSFILE  = make.ps
DVIFILE = make.dvi

$(PSFILE) : $(DVIFILE)
        dvips $(DVIFILE) -o

make.ps  : make.dvi
make.dvi : make.tex
------------------------------------------

[예제 12] 에서는 .tex 와 .dvi 를 처리하는 루틴이 자동적으로 동작을 하게 된다. Makfile 을 한번 동작시켜보자.

% make
latex make.tex
....
dvips make.dvi -o  <-- OK

[예제 11] 과 [예제 12] 는 하는일은 같다. 하지만 [예제 12]는 매크로를 사용함으로써 나중에 내용을 바꿀때 [예제 11]보다 편하다는것을 이해 했으면......

--다음편 예고-------------------------------------------------------
무엇인가를 글로 설명한다는 것이 참 힘드네요..

사실 오늘 한것만 가지고도 Makefile 에 대한 어느 정도의 지식을 갖추었다고 말할수가 있습니다. 자신만의 Makefile 을 한번 작성해보시죠. 프로그램도 괜찮고, .tex 화일을 .ps 화일로 만드는 과정을 Makefile 로 만들어 보는것도 좋은 연습이 될것입니다.

다음편에서 여러분에게 소개해 드릴것은 make 옵션, makefile 작성 지침 (guideline), make 사용시에 나타나는 에러의 원인과 그 대처방법이 될 것 같군요. ( 아직 확정된것은 아니지만서두.) Makefile 에 관한 입문 과정은 다음편으로 끝내고. 4강부터는 약간 고급스러운 기능을 강좌하도록 하겠습니다. 많이 읽어주세요.  댕스~ 였습니다.