팜 프리 스마트폰(2009년 6월 4일 기준 스프린트 2년 약정으로 200달러)은 웹OS 운영 체제와 함께 지난 1월 발표된 후부터 폭발적인 관심을 끌어왔다. 팜 프리는 완벽하진 않지만 적어도 실망스럽지는 않다. 웹OS 인터페이스는 깔끔하고 직관적이다. 그러나 필자가 발견한 중요한 문제는 하드웨어 자체의 문제였다. 웹OS 인터페이스는 깔끔하고 직관적이다.

 

하드웨어
유광 검정색 프리는 부드러운 곡선이 돋보이며 전면의 대부분은 3.1형 320 x 480 터치 화면이 차지한다. 화면은 위로 밀어 올리는 방식인데, 펼치면 사용자쪽으로 약간 기울어진다. 반사를 줄이고 사용감을 높이기 위한 디자인이다. 특히 주변이 밝은 경우 이 약간의 기울어짐이 효과를 발휘해 가독성을 높여준다. 크기는 9.9cm x 5.8cm x 1.8cm로 아무 부담 없이 주머니에 넣고 다닐 수 있다. 특히 여성용 청바지의 뒷주머니에도 무리 없이 들어간다. 풀 QWERTY 키보드를 채용한 스마트폰으로서는 드문 휴대성이다.


 


그러나 작은 크기를 위해 키보드의 사용 편의성은 희생됐다. 기계식 키보드가 있다는 것 자체는 반가운 부분이지만 디자인은 불편하다. 세로로 펼쳐지는 QWERTY 키보드의 모양과 느낌은 팜 센트로와 거의 비슷하다.

키는 유광 검정색, 문자는 주황색이며 내장된 키패드는 다른 색으로 구분되도록 했다. 키는 약간 안쪽으로 들어간 형태라서 옆과 아래쪽의 돌출된 베젤이 입력에 방해가 되는 경우가 많다. 또한 가장 윗줄이 화면 가장자리와 너무 가까이 붙어 있어 누르기가 불편하다. 키보드는 부드럽게 밀려 나오지만 튼튼하다는 느낌은 없어서 장시간 사용할 경우 망가지지 않을까 걱정된다.


 


손이 작은 편인 필자에게는 키의 크기가 불편할 정도로 작게 느껴지지는 않았지만 동료들 중 일부는 답답함을 느꼈다. 게다가 키감은 센트로와 마찬가지로 끈적거리고, RIM 블랙베리 제품과 같은 딱딱 끊어지는 맛이 없다. 프리에는 터치 키보드가 없으므로 다른 개발업체에서 키보드 애플리케이션을 개발할 때까지는 이 키보드를 계속 사용해야 한다.

장점 중 하나는 일부 제품에서 볼 수 있는 입력과 화면 표시 사이의 지연이 없다는 점이다. 즉, 키보드로 입력하면 즉각 텍스트가 화면에 표시된다.

프리에는 전화를 걸기 위한 터치 숫자 패드가 있다. 스프린트 3G 네트워크를 통한 통화 품질은 전체적으로 우수했지만 한 번은 일반 전화와의 통화에서 반향이 발생했다. 상대방 쪽에서도 필자의 목소리가 풍부한 음량으로 또렷하게 들렸다. 통화가 중간에 끊어진 적은 없고 필자와 상대방 쪽에서 모두 잡음은 들을 수 없었다. 아쉽게도 배터리 성능은 그다지 좋지 못했다. PC 월드 배터리 성능 테스트에서 프리의 평균 통화 시간은 5시간 17분으로 "보통(Fair)"을 기록했다. 5시간 38분의 통화 시간을 기록한 아이폰에 비해 약간 더 떨어지는 수준이다.

키보드 외에 또 하나 실망스러운 부분은 착탈식 메모리가 없다는 점이다. 8GB 메모리가 고정된 상태로 제공된다. 아이폰 3G와 달리 프리에는 16GB 모델이 없다(적어도 현재로서는). 원래 팜은 USB 케이블로 프리를 PC에 연결하면 PC에서 대용량 저장 장치로 인식되므로 바로 파일을 전송할 수 있다고 밝혔다. 그러나 통신업체 측의 제한으로 인해 이 기능은 프리에서 사용할 수 없다. 참고로 아이폰 3G 역시 이러한 연결 기능을 갖추고 있으나 AT&T의 제한으로 사용할 수 없는 상태다.


 


프리 조작은 용량성 터치스크린과 화면 아래쪽에 위치한 제스쳐 영역 상에서 몇 가지 기본 제스처를 통해 이뤄진다. 최근 일반화되고 있는 스크롤, 페이지 넘기기, 뒤로 가기, 집기 및 확대 기능을 제스처로 이용할 수 있다. 제스처 영역은 이전 팜의 전용 탐색 버튼 및 컨트롤을 대체한다.

프리의 전면에 위치한 유일한 버튼인 동그란 중앙 버튼은 홈 버튼 역할을 한다. 전화기 위쪽에 음소거를 위한 슬라이더 스위치가 그대로 유지된 점은 반가웠다. 또한 여행자에게 유용할 비행기 모드 바로가기 키도 있으며 이 키 옆에는 표준 3.5mm 헤드폰 잭이 있다. 볼륨 조절 버튼은 오른쪽, 미니 USB 충전 포트는 왼쪽에 위치하며 뒷면에는 300만 화소 카메라 렌즈, 큼직한 셀카용 거울, 착탈식 배터리가 있다.


 


웹OS
팜은 프리와 함께 그동안 연기를 거듭한 새로운 운영 체제인 웹OS도 출시한다. 필자가 테스트한 바에 따르면 웹OS는 매끄럽고 잘 디자인된 스마트폰 플랫폼으로, 애플 아이폰 OS나 구글 안드로이드에 비해서도 손색이 없다.


 


다만 몇 가지 문제점도 있다. 무엇보다 웹OS 자체는 반응이 신속하지만 애플리케이션 로딩 속도가 느린 경우가 있고, 일부 기능의 분류나 배치가 직관적이지 않고 혼란스럽다.


 


홈 화면 인터페이스의 아래쪽에는 사용자 설정이 가능한 애플리케이션 위젯이 위치한다. 위젯을 두드리면 해당 애플리케이션이 바로 뜬다. 다만 사용자가 임의로 제거할 수 없는 런처(Launcher) 단축키를 제외하고 한 번에 최대 4개의 단축키만 표시할 수 있다.


 


구글 안드로이드와 마찬가지로 팜 웹OS는 완벽하게 멀티태스킹을 지원한다. 아이폰 OS 2.0에서는 아직 구현되지 않은 기능이다. 프리는 멀티태스킹을 카드 다발 형태로 시각화하여 관리한다. 열려 있는 각 애플리케이션을 한꺼번에 보고, 원하는 대로 섞고, 선택한 애플리케이션을 닫을 수 있다.


 


이러한 모든 동작은 실제 카드를 다룰 때의 손동작을 흉내낸 제스처를 통해 수행한다. 카드 보기로 최소화한 경우에도 애플리케이션은 활성 상태를 유지하므로 사용자가 다른 작업으로 전환해도 실시간으로 변경 사항이 적용된다. 전체적으로 이러한 카드 배열 방식은 재미있었으며 직관적으로 여러 애플리케이션을 관리할 수 있었다.


 


웹OS에는 편리한 알림 기능도 있다. 이 기능은 전화가 오거나 문자 메시지 또는 이메일이 도착하면 현재 열려 있는 애플리케이션에 방해가 되지 않는 선에서 화면 아래쪽에 작은 팝업 창을 띄운다(구글 안드로이드와 비슷함).


 


이 알림 기능은 유용하긴 하지만 알림의 위치(빠른 실행(Quick Launch) 도구 모음 아래)는 약간 불편하다. 실행 도구 모음을 누르려다 알림을 누르거나 그 반대의 경우가 자주 발생한다. 개인적으로는 알림이 화면 위쪽을 가로지르는 형태로 표시되는 구글 안드로이드의 레이아웃이 더 마음에 든다. 프리의 알림 팝업 창은 대기 화면에도 표시된다.


 


팜 OS의 팬이라면 프리에도 복사-붙여넣기 기능이 그대로 유지됐다는 사실이 반가울 것이다. 키보드의 Shift를 누른 채로 터치스크린에서 원하는 텍스트 블록을 선택한 다음 화면 왼쪽 상단에서 애플리케이션 메뉴를 열고 복사, 잘라내기 또는 붙여넣기를 선택하면 된다.


 


 


소셜 네트워킹 시너지
웹OS의 가장 중요한 구성 요소 중 하나는 다양한 출처의 정보를 하나의 통합 보기로 동기화하고 종합할 수 있는 기능이다. 팜에서 "시너지"라고 명명한 이 기능은 연락처, 이메일 및 메시징 애플리케이션에 들어가 있다. 예를 들어 프리를 구글, 페이스북 및 마이크로소프트 익스체인지 계정과 동기화하면 이러한 각 계정으로부터 연락처를 가져와 프리의 연락처 애플리케이션에 모두 표시한다.


 


모든 연락처를 하나의 목록으로 만드는 기능은 표면적으로는 좋은 접근 방법으로 보인다. 그러나 실제 사용하면서 필자는 이 기능이 지나치게 사용자 의도를 앞서나간다고 느꼈다.  솔직히 말하자면 정말 필요한 기능인지에 대해서도 의문이 들었다. 필자의 경우 자주 연락하지 않는 페이스북 친구들까지 연락처 목록에 표시되는 것은 오히려 불편하게 느껴졌다. 또한 특정 연락처 목록만 로드할 방법이 없다. 전체를 다 로드하거나 아무것도 로드하지 않거나, 둘 중 하나다.


 


아이칼, 어드레스 북(맥용) 또는 데스크톱 아웃룩이나 팜 데스크톱과 같은 데스크톱 애플리케이션을 사용해서 연락처, 일정, 작업을 저장하고 싶다면 데스크톱 소프트웨어를 구글 계정에 동기화하는 써드 파티 애플리케이션을 다운로드하면 된다. 그런 다음 구글 계정을 프리에 동기화할 수 있다. 또한 써드 파티 애플리케이션인 포켓미러(현재 팜 애플리케이션 카탈로그를 통해 제공됨)를 사용하면 와이파이를 통해 프리를 아웃룩에 직접 동기화할 수도 있다.


 


달력 애플리케이션은 색 조합 기능과 복수 달력을 지원한다. 주목할 만한 부분은 구글 및 페이스북 달력과 같은 공용 또는 특정 달력에 가입할 수 있다는 것이다. 예를 들어 프리를 사용해서 구글 달력에 어떤 항목을 추가하면 이 정보는 구글의 달력 웹 사이트에 있는 세부 정보와 동기화된다(온라인에 표시될 때까지 몇 시간이 걸리긴 하지만).


 


시너지 이메일 애플리케이션을 사용하면 여러 이메일 계정을 손쉽게 확인 및 검색할 수 있다. 연락처를 선택하면 웹OS가 해당 연락처 정보를 사용하여 이메일 메시지에 이 정보를 자동으로 입력한다. 또한 여러 이메일 계정을 설정해 사용하는 경우에는 메시지를 작성하는 중에도 보내는 데 사용할 주소를 선택할 수 있다.


 


메시징 애플리케이션은 이제 SMS와 인스턴트 메시징을 하나의 틀로 제공한다. 현재 팜 OS 기반 전화기에서와 마찬가지로 대화는 스레드로 누적되므로 여러 시스템에 걸쳐 특정 연락처와 계속 이어지는 대화를 유지할 수 있다. 예를 들어 AOL 인스턴트 메신저로 대화를 시작한 후 상대방이 오프라인이 될 경우 텍스트로 이 대화를 이어나갈 수 있다.


 


풀 HTML 웹 브라우저에서 페이지는 깔끔하게 렌더링된다. 메모리가 허용하는 한 수에 제한 없이 원하는 만큼 브라우저 창을 열 수 있으며, 이전과 마찬가지로 비행 등의 오프라인 상태에서 볼 수 있도록 페이지를 저장할 수도 있다. 이 기능은 이전부터 경쟁 제품에는 없는 팜 OS 장치만의 커다란 장점이다.


 


메시징 소프트웨어 외에 프리에는 유튜브, 구글 맵스, 아마존 MP3 스토어, PDF 뷰어, 문서 뷰어, 계산기, 작업 목록, 메모판 등의 몇 가지 다른 애플리케이션도 제공된다. 또한 팜 애플리케이션 카탈로그를 통해 더 많은 애플리케이션을 구할 수 있다. 스프린트 TV, 스프린트 NASCAR 프로그램과 같은 스프린트 애플리케이션도 프리에 미리 설치돼 있다.


 


멀티미디어


미디어를 프리와 동기화하는 작업은 간단하다. 아이튠즈를 통해 음악을 가져올 수도 있고 마우스로 끌어서 놓는 방법으로 가져와도 된다. 미디어 플레이어는 표준적인 기능을 제공한다. 가수, 앨범, 곡 또는 장르별로 뮤직 라이브러리를 볼 수 있고 앨범 아트를 보거나 재생 목록을 만들 수 있다. 물론 배경 작업으로 음악 애플리케이션을 실행할 수 있다.


 


프리는 MP3, AAC, AAC+, WAV, AMR 파일을 지원한다. 제품에 포함된 이어폰은 잡음 없이 깨끗한 소리를 들려주지만 베이스가 약하다. 프리 사용자는 구글 안드로이드 기반 T-모바일 G1과 마찬가지로 아마존의 모바일 뮤직 스토어를 이용할 수 있다. 이 뮤직 스토어에서 DRM이 적용되지 않은 곡을 프리로 간단히 다운로드할 수 있다.


 


프리의 화사한 화면을 통해 재생되는 동영상 품질도 뛰어나다. 프리에는 MPEG-4, H.263, H264를 지원하는 전용 비디오 플레이어가 있다. 미리 설치된 상태로 제공되는 유튜브 애플리케이션은 와이파이나 스프린트의 EvDO 네트워크를 가리지 않고 고품질 H.264 포맷으로 비디오를 전송한다.


 


카메라는 300만 화소에 LED 플래시를 제공하지만 일부 보급형 전화기에도 있는 줌 기능이 없다. 평범한 사양임에도 화질은 꽤 만족스럽다. LED 플래시의 성능도 우수해서 어두운 실내에서도 선명하고 비교적 정확한 색의 사진을 얻을 수 있었다. 실외 사진의 화질은 더 우수해서, 뛰어난 색 재현력에 이미지 노이즈와 왜곡도 잘 억제되는 모습을 보였다.


 


카메라에는 전용 셔터 버튼이 없으므로 화면에 표시되는 버튼을 눌러야 한다. 기계식 셔터 버튼이 없어서 촬영 시 불안정한 감이 있고 따라서 사진이 흔들리기 쉽다. 이 현상을 경감��키는 가장 좋은 방법은 키보드를 빼고 촬영하는 것이다. 이렇게 하면 촬영 시 전화기를 안정적으로 유지하는 데 도움이 된다. 카메라 화면에서 사진 앨범을 열 수 있는데, 아쉽게도 앨범은 웹OS에서 가장 반응이 느렸다. 사진 사이를 이동하는 것도 굼떴고, 가끔은 반은 현재 이미지, 반은 다음 이미지가 나타난 상태로 화면이 멈추기도 했다.


 


프리에는 2세대 아이폰과 마찬가지로 동영상 녹화 기능이 없다. 그러나 OS가 오픈 소스인 만큼 앞으로 동영상 녹화 애플리케이션이 나올 가능성은 충분하다.


 


하드웨어적인 결점을 제외하면 팜 프리의 느낌은 훌륭했다. 멋진 디자인과 부드러운 동작 등을 갖춘 이 스마트폰은 최근에 필자가 살펴본 제품 중에서 가장 만족도가 높았다.


 


"팜 프리, 아이튠즈와 동기화 가능"


'아이폰 킬러'로 주목받는 팜 프리가 아이폰만의 기능을 차용하는 전략을 채택한다. 아이폰 고유의 기능이었던 아이튠즈와의 동기화 기능까지 지원하는 것.


 


포춘의 필립 엘머 드와이트가 게재한 포스트에 따르면 팜 프리는 아이폰이나 아이팟처럼 아이튠즈에서 인식 및 동기화될 수 있다.


 


맥월드의 아이어쉬 아리아는 전직 애플 하드웨어 임원이었던 존 루빈스타인이 팜 프리 개발팀을 이끌고 있다는 점, 또 아이튠스가 비애플 계열의 MP3 플레이어도 지원했었다는 점에서 일견 예측 가능한 기능이라고 언급했다.


 


그러면서도 그는 팜 프리가 아이튠즈 및 맥 친화적인 기기로 등장한다는 고무적인 신호라고 평가했다.


 


 


팜 프리 출시 후 첫 주말, "10만대 판매 예상"


지난 6일 출시된 팜 프리(Palm Pre)가 주말 동안에 10만대 정도 판매됐을 것이라는 분석이 제기됐다.


 


스프린트 넥스텔(Sprint Nextel)과 베스트 바이(Best Buy) 및 다른 매장에 제한된 물량만 풀려 이번 론칭은 일반적으로 약하다는 평가를 받고 있다. 한 업계 관계자가 ‘아이폰 주니어’라고 표현한 팜의 새로운 스마트폰은 판매가 시작된 날 매장 앞에 많은 사람들이 줄을 서 있는 등 관심을 받았다. 가격은 100달러 메일인리베이트(mail-in rebate) 후 가격 200달러로 책정됐고, 스프린트와 2년 약정을 맺어야 한다.


 


출시일에 프리의 제한된 물량을 확보하기 위해 소비자들이 각지의 매장에 줄을 서있다는 많은 보도를 기반으로 ABI 리서치의 분석가인 케빈 버든은 첫 주말 프리 판매량이 5만~10만대 사이로 목표치를 달성했을 것이라고 추정했다.


 


버든은 “8만대 혹은 10만대가 판매됐을지는 알 수 없다. 하지만 5만~10만대 사이에 있는 것은 확실”하다고 말했다.


 


스프린트는 이와 관련해 8일 저녁 팜 프리의 첫 주말 판매량이 스트린트에서 나온 제품 중 최고치를 경신했다고 전했지만, 정확한 판매량은 밝히지 않았다.


 


한편, 애플은 8일 기능을 향상시킨 아이폰 3GS를 출시해, 팜 프리와의 전면전이 예상된다.


 


 


팜 프리 분해해보니… “원가는 170달러"


지난 토요일 많은 사람들이 팜 프리를 구매해서 실제로 사용하기 위해 매장으로 몰려들었다. 하지만 래피드 리페어(Rapid Repair)와 아이픽스잇(iFixit)은 구매 즉시 팜 프리를 해부해 어떤 내용물이 채워져 있는지 확인해 보았다.


 


 


<출처: 래피드 리페어>


 


아이픽스잇이 처음으로 살펴보았던 것은 배터리 부분이다. 용량이 1150mAh인 이 배터리는 아이폰과 같았으나, 아이픽스잇은 충전시간이 팜 프리가 짧을 것으로 예상하면서, 프리의 처리과정 때문일 것으로 추정했다.


 


그 다음은 안테나를 살펴보았는데, 높은 신호품질을 보장하는 3차원으로 디자인되어 있다.


 


또, 아이픽스잇은 키보드의 무게가 32g으로 전체 무게의 25%를 차지한다는 것을 알아냈다. 아직까지 팜의 무게에 대한 불만은 없는 것으로 보아 팜이 모든 내용물을 이렇게 작은 디바이스안에 다 넣었다는 것이 매우 인상적이다.


 


스크린에 대해서 래피드 리페어는 LCD와 터치스크린이 서로 붙어있으며, 이들을 분리하는 방법은 없어 보였다면서, 만일 이 중 하나라도 망가지면 둘 다 교체해야 할 것이라고 설명했다.


 


 


<출처: 래피드 리페어>


 


마지막으로 넥사스 인스트루먼트의 OMAP3 플랫폼인 침수방지 센서를 살펴보았다. 로직 보드(logic board)는 아이폰보다 확연히 작아서 팜이 어떻게 이 모든 구성품을 9.9cm x 5.8cm x 1.8cm에 넣을 수 있었는지 설명된다.


 


래피드 리페어는 이런 팜 프리의 구성품을 통틀어 가격이 약 170달러 정도 될 것이라고 추정했다. 이는 2008년 7월 아이서플라이(iSuppli)가 추정한 아이폰의 구성품 가격과 거의 같은 수준이다. 물론, 여기에는 디자인이나 공정에 대한 비용은 포함되지 않은 것이다.


 

출처 : 오라닉스 ( http://cafe.daum.net/oranix )

1. bash에서의 쉘변수와 환경변수(1) 쉘변수
 1) 개요: 말 그대로 특정한 쉘 즉 bash에서만 적용되는 변수를 말한다.
 2) 특징
  ㄱ. 지정하는 방법은 '변수명=값' 형태로 지정하면 된다.
    예) [posein@www posein]$ COLOR=red
  ㄴ. 변수값을 출력할 때는 변수명 앞에 $을 붙여 echo명령을 사용하면 된다.
    예) [posein@www posein]$ echo $COLOR
      red
(2) 환경변수: 모든 쉘에 영향을 미치는 변수라는 것을 제외하고는 쉘변수와 지정방법이나 특징이
        유사하다.
(3) bash에서 쉘변수를 환경변수화시키기: export명령을 사용하면 된다.

2. 일반적인 환경변수목록(1) 환경변수
 1) HOME : 사용자의 홈디렉토리
 2) PATH : 실행파일을 찾는 경로
 3) LANG : 프로그램 사용시 기본 지원되는 언어
 4) PWD : 사용자의 현재 작업하는 디렉토리
 5) TERM : 로긴 터미널 타입
 6) SHELL : 로그인해서 사용하는 쉘
 7) USER : 사용자의 이름
 8) DISPLAY : X 디스플레이 이름
 9) VISUAL : visual 편집기의 이름
 10) EDITOR : 기본 편집기의 이름
 11) COLUMNS : 현재 터미널이나 윈도우 터미널의 컬럼수
 12) PS1 : 명령프롬프트변수
 13) PS2 : 2차 명령프롬프트이다. 명령행에서 를 사용하여 명령행을 연장했을 때 나타난다.
 14) BASH : 사용하는 bash 쉘의 경로
 15) BASH_VERSION : bash의 버전
 16) HISTFILE : history 파일의 경로
 17) HISTFILESIZE : history 파일의 크기
 18) HISTSIZE : history에 저장되는 갯수
 19) HISTCONTROL : 중복되어지는 명령에 대한 기록 유무를 지정하는 변수이다.
 20) HOSTNAME : 호스트의 이름
 21) LINES : 터미널의 라인 수
 22) LOGNAME :로그인이름
 23) LS_COLORS : ls 명령의 색상관련 옵션
 24) MAIL : 메일을 보관하는 경로
 25) MAILCHECK : 메일확인시간
 26) OSTYPE : 운영체제 타입
 27) SHLVL :쉘의 레벨
 28) TERM :터미널종류
 29) UID : 사용자의 UID
 30) USERNAME : 사용자이름
(2) 사용예
 1) [posein@www /]$ mkdir $HOME/backup
   [posein@www /]$ ls -ld $HOME/backup
   drwxrwxr-x   2 posein  posein     4096 1월 15 01:31 /home/posein/backup
 2) [posein@www /]$ echo $PS1
   [u@h W]$
    => 프롬프트 형식
      d : '요일 달 날짜'형태로 나타내준다. (예 "Wed Jan 15")
      h : 호스트이름을 보여준다. 보통 '.'를 사용한 이름인 경우 첫번째 '.'까지 보여준다.
      H : 호스트이름을 보여준다.
      l : 쉘의 터미널 장치의 이름을 보여준다.
      s : 쉘의 이름을 보여준다.
      t : 24시 형태의 현재 시간을 보여준다. (예 HH:MM:SS)
      T : 12시 형태의 현재 시간을 보여준다. (예 HH:MM:SS)
      @ : am/pm 12시 형태의 현재시간을 보여준다.
      u : 현재 사용자의 이름을 보여준다.
      w : 현재 작업디렉토리를 보여준다.
      W : 현재작업디렉토리의 마지막 디렉토리만 보여준다.
      ! : 현재 명령의 히스토리 넘버를 보여준다.
      : 를 보여준다.
 3) [posein@www posein]$ PS1="[u@t W]$ "
   [posein@00:53:51 posein]$
    => 프롬프트에서 호스트이름대신에 현재시간을 표시하도록 설정하였다.

3. 환경변수관련 명령
(1) set : shell변수를 표시하고 값을 지정할 수 있다. C-shell에서는 변수와 값지정시에 필수적으
      로 사용해야 하지만, Bash에서는 변수와 값지정시에 꼭 set 명령을 지정하지 않아도 된다.
 1) 사용법
  set [option] [argument]
 2) option
  -o : 현재 set옵션의 상태를 표시한다.
 3) 사용예
  ㄱ. set
    => 옵션이나 인자가 주어지지 않으면 이미 지정된 shell변수와 함수이름,값이 표시된다.
  ㄴ. set -o
    => 현재 set옵션의 상태가 표시된다.
 4) 응용예
  [posein@www posein]$ a=1          // bash에서는 set 명령없이 "변수=값" 형태로 지정
                              하면 된다. 확인은 인자없이 set 이라고 입력한다.
  [posein@www posein]$ echo $a
  1
   => 변수로 선언되었으므로 $a하면 1이라는 값이 출력된다.
  [posein@www posein]$ /bin/csh       // 임시로 C-shell로 전환.
  [posein@www ~]$
   => C-shell로 전환하면 프롬프트로 바뀜을 알 수 있다.
  [posein@www ~]$ b=2
  b=2: Command not found.
   => bash에서 변수지정하는 것처럼 하면 오류가 나타남을 알 수 있다.
  [posein@www ~]$ set b=2
   => C-shell 계열에서는 변수와 값지정시 set 명령을 사용해야 한다. 확인하려면 인자없이 set
    이라고 입력한다.
  [posein@www ~]$ echo $b
  2
    => 변수로 선언되었으므로 $b하면 2라는 값이 출력된다.

(2) env : 환경변수에 대한 정보를 보여준다.
 1) 환경변수란 : 로그인할 때나 새로운 쉘을 파생시킬 때 쉘의 환경을 정의하는 중요한 역할을
           수행한다. env를 실행하면 환경 변수 설정값들을 확인할 수 있고 또한 각 환경
           변수를 나타낼 때 변수이름앞에 $를 붙인다.
 2) 사용예
  [root@www /root]# env
   => 현재 시스템의 환경변수를 보여준다.
 3) 환경변수의 설정 : 값을 지정한후 export해야 한다. 현재 리눅스의 bash에서는 export를 생략
              해도 반영된다.
  예) 패스변경하기
    [posein@www posein]$ echo $PATH
    /usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/posein/bin
      => 현재 패스를 확인하면 홈디렉토리를 없다. 홈디렉토리를 추가해보자.
    [posein@www posein]$ PATH="$PATH:/home/posein"
    [posein@www posein]$ export PATH
    [posein@www posein]$ echo $PATH
    /usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/posein/bin:/home/posein
(3) export : 쉘변수를 환경변수로 만들어주는 명령이다. 현재 리눅스 bash에서 일시적인 반영시에
        는 생략해서 사용할 수 있다.
 1) 사용법
  export 환경변수=변수값
 2) 사용예
  ㄱ. [root@www root]# export PATH="$PATH:/usr/local/apache/bin"
      => 현재 설정된 패스값에 /usr/local/apache/bin이라는 경로를 추가한다.
  ㄴ. [posein@www posein]$ PATH="$PATH:/home/posein"
      => 현재 설정된 패스값에 "/home/posein"이라는 경로를 추가한다. export명령을 생략해서
       사용해도 된다.
 3) 참고: export 명령은 쉘변수를 환경변수로 만들어 준다. 그러나, 쉘에서 export로 선언하여
      사용한 뒤에 로그아웃하게 되면 초기화된다. 따라서, 해당 환경변수의 값을 계속적으로
      반영되도록 하려면 /etc/profile(전체시스템)이나 ~/.bash_profile(개인사용자)파일안에
      선언하면 된다.

(4) unset : 선언된 변수를 제거하는 데 사용한다.
 1) 사용법
  unset 변수이름
 2) 사용예
  [root@www /root]# TEL=042
  [root@www /root]# echo $TEL
  042
  [root@www /root]# unset TEL
  [root@www /root]# echo $TEL  // 변수가 제거되었으므로 아무값도 출력되지 않는다.

(참고) bash에서는 환경변수를 만들 때 변수 값을 설정한 후, 환경에 변수를 익스포트(export)하는
    두 단계를 거친다.
 * 사용예
  [posein@www posein]$ echo $LANG     // 언어관련 환경변수값 확인
  ko_KR.eucKR
  [posein@www posein]$ date
  수 5월 21 01:28:56 KST 2003       // 한글로 표시된다.
  [posein@www posein]$ LANG=euc_UN    // 영어로 변경
  [posein@www posein]$ export LANG    // 일시적으로 변경할 경우에는 생략가능
  [posein@www posein]$ date
  Wed May 21 01:29:07 KST 2003       // 영어로 표시된다.


4. 명령어 히스토리(command history)
(1) history에 대하여
 1) 설명: bash에서는 입력하여 실행했던 모든 명령들은 히스토리 리스트 버퍼에 스택으로 저장된
      다. 이 기능은 반복하여 입력하거나 명령을 수정할 때 유용하게 쓰인다. 사용법은 방향키
      위/아래를 누르면서 사용가능하다. 히스토리 파일은 각 사용자의 홈 디렉토리에
      .bash_history라는 이름으로 존재하며 쉘 실행 중에는 메모리에만 명령어 히스토리를
      기억하고 있다가 로그아웃시에 .bash_history파일에 저장한다.
 2) 사용예
  [posein@www posein]$ history
   => 입력한 명령어들의 리스트를 보여준다.
(2) history 관련 변수
 1) 종류
  ㄱ. HISTSIZE : 히스토리 스택의 크기가 지정되어 있다. 단위는 명령의 개수이다. 이 변수의
            설정값을 변경했을 경우 history명령을 내리면 해당개수만큼만 출력된다. 또한
            방향키로 검색했을 경우에는 설정한 명령한 개수만 검색된다.
  ㄴ. HISTFILESIZE : 실질적인 히스토리파일의 크기이다.
  ㄷ. HISTFILE   : 히스토리 파일의 위치를 보여준다.
  ㄹ. HISTCONTROL : 중복되어지는 명령에 대한 기록 유무를 지정하는 변수이다.
 2) 사용예
  ㄱ. [posein@www posein]$ echo $HISTFILE
     /home/posein/.bash_history
  ㄴ. [posein@www posein]$ HISTSIZE=1
      => 실질적인 히스토리 파일의 스택크기가 1이 되므로 방향키로 조회해도 나오지 않는다.
(3) ! 과 히스토리 명령문 : 느낌표(!)를 이용하여 실행할 수 있다.
 1) 사용법
  !! : 마지막으로 실행했던 명령문을 실행한다.
  !n : n번째 실행한 명령문을 실행한다.
  !-3 : n번째 이전에 실행했던 명령문을 실행한다.
  !string : 가장 최근에 'string(문자열)'으로 시작하는 명령문을 실행한다.
  !?string? : 가장 최근에 실행했던 명령문중 string을 포함하고 있는 명령문을 실행한다.
          배포판에 따라 string뒤에 ?는 생략가능하다.
  ^string1^string2 : 마지막 실행 명령문의 string1을 string2로 대체한 후 실행한다.
 2) 사용예
  ㄱ. [posein@www posein]$ pwd
     /home/posein
     [posein@www posein]$ !!
     pwd
     /home/posein
      => pwd가 실행된다.
  ㄴ. [posein@www posein]$ !-4
     date
     수 5월 21 01:51:08 KST 2003
      => history 스택을 거슬러 4만큼 올라가서 해당 명령을 실행한다. 현재의 예제는 date
       명령임을 알 수 있다.
  ㄷ.[posein@www posein]$ !100
      => history의 번호중에서 100번 명령을 실행한다.
  ㄹ. [posein@www posein]$ set
     .....
     [posein@www posein]$ !s
      => 가장 최근에 's'로 시작하는 set명령이 실행된다.
  ㅁ. [posein@www posein]$ ls -alF
     .....
     [posein@www posein]$ !?al
      => ls -alF가 실행된다.
  ㅂ. [posein@www test]$ ls
     a.txt
     [posein@www test]$ cp a.txt b.txt
     [posein@www test]$ ^b.txt^c.txt
     cp a.txt c.txt
     [posein@www test]$ ls
     a.txt b.txt c.txt
(4) 참고 - history관련 테크닉
 1) [CTRL] + [r]
    => 명령프롬프트상태에서 이 키 조합을 누르면 검색할 수 있는 명령프롬프트가 뜬다. 이 때
     특정한 문자를 입력하면 가장 최근에 그 문자로 수행한 명령을 보여준다.
 2) [ESC] 후에 [.] 또는 [ALT] + [.]
    => 최근에 사용된 인자(argument)를 붙여준다. 텔넷으로 접속한 경우에는 [ALT]+[.]은 사용할
     수 없다.
 3) export HISTCONTROL=ignoreboth
    => 중복되어지는 명령어는 히스토리에 기억하지 않는다. 명령행에 입력하거나 계속적으로
     반영시키려면 .bashrc파일에 기록한다.

5. alias
(1) 개요 : 명령어에 별명(alias)를 만드는 것이다. 어떠한 명령에 기본으로 옵션을 추가시키거나
      자신만의 독특한 명령어를 만들 수 있다. 기본적으로 alias만 입력했을 경우에는 현재
      설정된 alias를 보여준다.
(2) 사용법
  alias 별명이름='실행될 명령의 정의'
(3) 사용예
 1) alias
    => 현재 설정된 alias를 보여준다.
 2) alias rm='rm -i'
    => rm명령에 기본으로 -i옵션을 부여하여 rm명령을 실행시킬때마다 확실히 지울 것인지 물어
     본다.
 3) unalias rm
    => rm에 설정된 ailas를 해제한다.
(4) 특징
  1) 일반쉘상태에서 alias를 설정한 뒤 로그아웃하면 그 설정은 무효가 된다.
  2) alias의 해제는 unalias명령을 이용한다.
  3) alias의 설정을 지속적으로 반영시키려면 ~/.bashrc파일안에 설정하면 된다.
(참고) ~/.bashrc파일안에 설정하면 다음 로그인부터 그 값이 반영된다. 만약 즉시 반영하고 원할
    경우에는 'source .bashrc'를 실행시키면 된다.



출처 - 오라닉스


 

1. 개요

make는 command generator이다. Description file과 몇 개의 일반적인 템플릿을 사용하여 UNIX shell이 수행 가능한 command sequence를 만든다. 이러한 command는 software development project를 구성하는 파일들의 유지 관리와 연관되어 있다. 여기서 유지 관리는 다수의 프로그램 소스로 구성된 프로그래밍 시에 현재 상태를 보고, 임시화일을 제거, 최종 프로그램의 실행화일을 만드는 전과정을 망라하는 작업을 말한다.

make는 화일간에 존재하는 종속성(dependency)을 정리하는데 사용할 수가 있다. 비교적 적은 수의 화일을 포함하는 소프트웨어 프로젝트라도 프로젝트내에 존재하는 화일간에는 여러 형태의 종속성이 존재할 수 있다. 예를 들어 프로그램은 오브젝트화일과 라이브러리를 링크하여 생성이 되고, 오브젝트는 다시 C나 Pascal같은 고급언어나 assembly언어의 소스에서 생성이 된다. 만약 하나 이상의 소스화일을 변경하는 경우 다시 컴파일하고 다시 링크시켜야 한다. 이러한 일련의 일은 프로젝트 수행 시 빈번히 발생한다. 이러한 일들을 자동화하는 것이 make utility이다. 화일간의 종속관계에 대한 description file을 작성하면 make가 관련된 update를 수행한다. 이러한 update는 최종 갱신된 관련화일을 화일 시스템을 통하여 검사함으로써 file A가 file B에 종속적이고 file B가 file A의 갱신 이후에 변경이 된다면 file A를 다시 구성(recompile, relink, edit,library교환)하는 메커니즘에 의해 수행이 된다.

2. Makefile의 작성법

make가 사용하는 description file은 임의의 이름을 가질 수가 있지만 기본적으로 Makefile또는 makefile이라는 이름을 사용한다. 다음과 같은 명령어

$make program

은 program의 최신 버전을 만들고자 할 때 사용한다. program이 실행 가능한 화일이라면 이러한 명령어를 통하여 실행화일을 만드는데 필요한 컴파일, 링크등을 수행하게 된다. 많은 cc 컴파일러 명령어를 커맨드 라인상에서 입력하는 대신에 make를 통하여 이러한 작업을 자동적으로 수행하게 된다.

앞으로의 설명을 위하여 다음과 같은 몇 가지 용어를 우선 정의한다. 먼저 target은 위에서 말한 program과 같이 만들고자 하는 대상이고, dependents(또는 prerequisite)는 이러한 target에 만들기 위하여 참여하는 객체를 말한다. 이러한 dependent는 다른 dependent의 target이 될 수도 있다.

make는 source-to-object, object-to-executable같은 종속관계의 계층적 구조(hierarchy of dependency)를 인식하여 필요한 작업을 수행하며, 이러한 종속관계의 계층적 구조는 앞에서 언급한 Makefile(또는 makefile)이라 불리우는 description file에 기술한다. make는 이러한 종속관계의 계층적 구조 뿐만 아니라 수행환경(environment)에서 정의된 환경 변수(environment variable)의 값을 사용하고, make에 존재하는 종속성을 결정할 수도 있다. 이러한 일련의 작업은 file의 존재 여부, file의 최종 갱신 시간과 make에 내장된 규칙을 사용하여 수행이 된다.

2.1 Description file

다음과 같은 예를 보자.

예)

다음과 같은 화일로 구성되는 프로그램 prog에 대하여

C language source file - main.c, iodat.c, dorun.c

Assembly language code - lo.s

library routines in - /usr/kimkk/lib/crtn.a

명령어 입력을 통하여 프로그램 prog을 생성하기 위해

$ cc -c main.c

$ cc -c iodat.c

$cc -c dorun.c

$as -o io.o io.s

$cc -o prog main.o iodat.o dorun.o lo.o /usr/kimkk/lib/crtn.a

의 명령어 입력이 필요하다면 다음과 같은 description file (Makefile)을 작성하여 이러한 작업을 자동화할 수가 있다.

1 prog : main.o iodat.o dorun.o lo.o /usr/kimkk/lib/crtn.a <- dependency line

2 cc -o prog main.o iodat.o dorun.o lo.o /usr/kimkk/lib/crtn.a <- command(rule) line

3 main.o : main.c

4 cc -c main.c

5 iodat.o : iodat.c

6 cc -c iodat.c

7 dorun.o : dorun.c

8 cc -c dorun.c

9 lo.o : lo.s

10 as -s lo.o lo.s

앞의 라인 번호는 설명의 편의를 위하여 삽입한 것이고 실제로는 삽입하지 않는다. 먼저 첫번째 줄과 같이 콜론(:)으로 구성된 줄을 dependency line(또는 rule line)이라고 하며 콜론의 좌측에 target이 콜론의 우측에 target의 생성에 관련되는 dependents를 나열한다. 다음 두 번째 줄과 같이 TAB으로 시작하는 줄을 command line이라 하며 command line을 바로 윗줄의 target을 생성하기 위하여 수행하여야 할 작업을 기술한 것이다. 그러므로 다음과 같은 명령어에 의하여

$make prog

을 통하여 prog를 만드는 수작업을 자동적으로 수행할 수가 있다. (description file명이 Makefile또는 makefile이 아닌 경우 -f option을 사용하여 지정할 수가 있다.)

2.2 Dependency checking

Make의 자동 수행의 이면에 make가 수행하는 다음과 같은 dependency checking이 있기 때문에 가능한 것이다. 앞의 description file을 사용하여 make가 prog를 생성하는 경우 바로 두 번째 줄의 command line이 수행되지는 않는다. 먼저 make는 prog화일의 존재 여부를 검사하여 존재하는 경우 main.o, iodat.o , dorun.o, lo.o와 /usr/kimkk/lib/crtn.a의 최종 갱신 시각을 검사하여 이들 중 하나라도 prog의 생성시간보다 뒤에 있는 것이 있는지를 검사한다. 이러한 검사는 운영체제의 화일시스템에서 유지하고 있는 모든 화일들에 있는 최종 갱신 시각을 사용하여 가능하다. prog의 생성시간이 모든 dependents의 생성 시각보다 뒤라면 (latest)라면 prog는 다시 만들 필요가 없고 그렇지 않다면 두 번째 줄의 command line을 사용하여 prog을 생성한다. 물론 재귀적으로 dependents도 다시 검사가 되어야 한다.

2.3 Minimizing Rebuild

다음의 예는 이러한 rebuild를 최소화하는데 make를 사용한 예이다. 다음과 같은 경우를 가정해보자. 멍텅구리 터미널(dumb terminal)또는 bit-mapped workstation에서 수행 가능한 data-plotting프로그램을 작성할 때 계산(calculation), 화일조작과 같은 일들이 두 가지 버전에서 공통적으로 상당 부분을 차지하고 이러한 부분의 작업은 basic.c에 저장하고 멍텅구리 터미널에서 사용하는 user input모듈은 prompt.c에 workstation에서 사용하는bit-mapped interface는 window.c에 저장된 경우 다음과 같은 description file을 사용하여 이러한 작업을 간단히 하고 rebuild도 최소화할 수 있다.

plot_prompt : baisc.o prompt.o

cc -o plot_prompt basic.o prompt.o

plot_win : basic.o window.o

cc -o plot_win basic.o window.o

baisc.o : basic.c

cc -c basic.c

prompt.o : prompt.c

cc -c prompt.c

window.o : window.c

cc -c window.c

먼저 workstation용의 프로그램을 만들 경우에는

$make plot_prompt

멍텅구리 터미널용 프로그램을 만들 때는

$make plot_win

을 사용하여 만들 수가 있다.

이 경우 처음 한번만 baisic.c의 컴파일이 필요하고 변경이 없는 한 이후에 컴파일 과정은 일어나지 않는다. 위의 description file도 커맨드 라인상의 입력에 비하면 많은 양의 일이 줄어든 것이지만 다음에서 설명할 매크로와 suffix rule을 사용하여 좀 더 간단해질 수가 있다.

2.4 make의 호출

앞의 예에서는 다음과 같은 점을 가정하였다.

. description file을 포함하는 모든 projetc file들이 동일한 디렉토리에 존재한다.

. description file명은 Makefile또는 makefile이다

. 이러한 모든 화일들은 make를 수행하는 시점에서 현재 디렉토리이다.

이상과 같은 상황에서 description내의 임의의 target을 생성하려면 다음과 같은 명령어를 입력하면 된다.

$make target

target을 생성하기 위한 관련된 rule line이 수행되고 이러한 수행의 상황은 터미널에 디스플레이된다. 만약 중간 화일의 일부가 존재하고 update-to-date한 경우에는 make는 관련된 command를 skip한다. 즉 make는 target을 생성하는데 필요한 최소한의 명령만을 수행한다. 어떠한 prerequisite도 갱신되거나 제거되지 않은 경우에는 make는 다음과 같은 메시지를 낸다.

'target' is up to date

생성하려 하는target이 description file내에 존재하지 않고 suffix rule에서 설명할 default rule에서 처리되지 못하는 경우 make는 다음과 같은 message를 낸다.

$make nontarget

make : Don't know how to make nontarget. Stop

또는

$make nontarget

'nontarget' is up to date

현재 디렉토리에 nontarget이라는 화일명이 존재하는 경우에 위와 같은 메시지를 받는다.

또한 다음과 같이 한번의 make호출에 여러 개의 target를 지정할 수가 있다. 이렇게 함으로써 여러 번의 make호출과 동일한 일을 수행할 수가 있다.

$make main.o dorun.o io.o

마지막으로 다음과 같이 target을 지정하지않는 경우에는

$make

description file내의 첫번째 taget을 생성한다.

2.5 기본적인 구문 규칙

description file내에서 모든 명령어는 탭(tab)으로 시작하여야 한다. Makefile과 관련된 가장 초보적이면서도 틀리기 쉬운 것이 명령어가 탭으로 시작하여야 한다는 것이다. 그러므로 명령어 라인이 아닌 주석(comment)나 dependency line은 탭으로 시작하면 안된다.

이러한 탭을 보기 위해서는 다음과 같은 cat명령어 옵션을 사용할 수 있다.

$ cat -v -t -e makefiel

where -v : print non-printing character

-t : print tab as ^I

-e : print end of line as $

또는 vi(Visual editor)에서 set list를 사용하여 탭과 end of line을 시각적으로 볼 수 있다.

한 줄 이상에 걸쳐서 기술할 때에는 라인의 끝에서 역슬래쉬(\)를 삽입하여 라인이 계속 이어짐을 나타낸다.

make는 빈 줄을 무시하고, #(compound sign)으로 시작하는 줄은 주석으로 인식한다.

명령어가 별개의 줄에 있을 필요는 없다. Dependency line에 명령어를 기술하고자 할 때는 세미콜론(;)으로 분리하면 된다.

plot_prompt : prompt.0 ; cc -o plot_prompt prompt.o

(명령어 라인이 탭으로 시작하지 않아도 되는 유일한 예외이다.)

단일 타겟이 여러 dependency lines에 걸쳐서 나타날 수도 있다.

file.o : /usr/src/file.c

cc -c /usr/src/file.c

...

...

file.o : global.h date.h

file.o를 실제로 생성하는 dependency line은 첫번째 dependency line이다. 이 경우 file.c의 변경이 없었다 하여도 *.h중 하나에 변경이 있었다면 file.o는 make의 입장에서 out of date하므로 다시 컴파일을 수행하게 된다. 여기서 주목할 점은 다중의 dependency lines을 사용한다 하여도 명령어와 연관된 dependency line만이 수행된다는 점이다.

Prerequisite이 없는 target(이 경우에도 콜론은 명시하여야 한다) 이것의 예로는 GNU package의 makefile에 흔히 있는 make clean의 clean target이다.

clean :

/bin/rm -f core *.o

make clean 명령 clean이라는 화일이 존재하지 않는 한 명령어 라인을 실행한다. 이는 make가 존재하지 않는 타겟을 out-of-target으로 취급하기 때문이다.

3 매크로(macro)의 사용

실제적인 description file에서는 앞에서 기술한 것보다 더욱 간졀하게 기술이 된다. 이는 매크로(macro)와 접미사 규칙(suffix rule)을 사용하여 가능한 것이다.

이 절에서는 매크로에 대하여 살펴본다.

먼저 매크로의 정의는 다음과 같이 한다.

name = text string

매크로 정의 이후에 매크로를 참조는 다음과 같이 한다.

$(name)

또는

${name}

$(name)또는 ${name}은 text string으로 해석되어 진다.

예)

LIBS = -lX11

objs = drawable.o plot_point.o root_date.o

CC = /user2/home/kimkk/bin/cc

23 = "This is the 23rd run"

OPT =

DEBUG_FLAG = #empty now, but assign -g for debugging

BINDIR=/user/home/kimkk/bin

plot : ${objs}

${CC} -o plot {DEBUG_FLAG} {objs} ${LIBS}

mv plot ${BINDIR}

은 다음과 같이 매크로 확장이 된다.

/user2/home/kimkk/bin/cc -o plot drawable.o plot_point.o root_date.o -lX11

mv plot /usr/local/bin

예에서 보듯이 description file내에서 매크로를 사용하면 반복적으로 사용되는 화일이나 명령어 옵션을 편리하게 참조하여 사용할 수가 있고 inconsistency없이 description file을 쉽게 수정 변경이 가능하다는 장점이 있다

3.1 매크로 구문규칙

매크로 정의는 equal sign(=)을 포함하는 줄이다. make는 equal sign앞의 매크로 이름과 equal sign이후의 text string을 연관시킨다. Test string은 single quote또는 double quote로 묶을 필요는 없다.( 23의 매크로정의에서는 string부분을 명시하기 위하여 quote를 사용한 것이다.)

매크로의 정의가 한 줄 이상일 경우에는 라인의 끝에서 마찬가지로 back slash(\)로 마크하여 라인이 계속 이어짐을 나타낼 수 있고, #으로 라인 끝까지 주석문으로 처리가 가능하다.

매크로 이름은 일반적으로 대문자로 사용하며 한 개의 문자로 이루어진 매크로 정의는 다음과 같이 참조할 수 있다.

A = XYZ

$A, $(A) 또는 $({A}로 참조

또한 매크로 정의 내에 다른 매크로의 이름을 포함할 수 있다.

ABC =XYZ

FILES = TEXT.${ABC}

OPT나 DEBUG_FLAG와 같이 equal sign이후에 스트링이 없는 경우에는 널스트림(Null string)이 매크로 이름에 할당이 된다.

3.2 내부적으로 정의된 매크로

make는 자주 사용되는 명령어에 대하여 매크로로 내부적으로 정의한다. 예를 들어 ${CC}는 C compiler로 {LD}는 linker로 인식한다.

다음과 같은 경우

basic.o = basic.c

${CC} -c basic.c

는 아래와 동일하다.

basic.o = basic.c

cc -c basic.c

내부적으로 정의된 매크로를 보려면

make -p

로써 내부적으로 정의된 모든 매크로를 볼 수가 있다.

3.3 Command line에서의 매크로 정의

다음과 같이 make 명령어 라인에서 매크로를 정의할 수 있다.

$ make kim_w DIR=/user2/home/kimkk/bin

make 명령어 라인에서 매크로를 정의할 때 2개 이상의 단어로 이루어지는 경우에는 quote로 묶어야 한다.

3.4 Shell variable의 사용

실행환경내에서 정의되고 있는 쉘 변수나 환경 변수를 description file내에서 사용 가능하고 이때 참조는 ${shell_variable}또는 $(shell_variable)형태로 한다.

%setenv DIR /user2/home/kimkk/bin <- csh, tcsh의 경우

또는

$ DIR=/user2/home/kimkk/bin; export DIR <- bsh인 경우

make description file내에서의 참조는

SRC = ${DIR}/../src

all :

cc -o kimkk_w ${SRC}/*.c

3.5 매크로 정의의 우선순위

다음과 같은 순서로 우선순위가 높아진다.

  • make가 내부적으로 정의한 매크로 정의
  • 현재 쉘의 환경 변수
  • description file내의 매크로 정의
  • make명령어 라인에서 입력한 매크로 정의

환경 변수가 낮은 우선순위를 가지는 것이 일반적으로 바람직하지만 다수의 프로젝트의 default definition을 변경하고자 할 때는 make를 여러 번 실행해야 한다. 이러한 경우에는 환경 변수를 셋팅하고 환경 변수가 우선순위가 높도록 -e 옵션을 make에 사용할 수 있다.

-e 옵션을 사용할 때 make가 사용하는 매크로의 우선순위는 다음과 같다

.

  • make가 내부적으로 정의한 매크로 정의
  • description file내의 매크로 정의
  • 현재 쉘의 환경 변수
  • make명령어 라인에서 입력한 매크로 정의

예)

description file

TEST = default_test

test : srcfile.c

${TEST} srcfile.c

환경 변수 셋팅

$ TEST=new_test; export TEST 또는 setenv TEST new_test

$make test

  • default_test srcfile.c

$make -e test

  • new_test srcfile.c

3.6 매크로 스트링 치환

다음과 같은 매크로 정의가 있는 경우

SRC = def.c redraw.c calc.c

description file내의 다음 명령어

ls ${SRC:.c=.o}

ls def.o redraw.o calc.o와 동일하다.

${SRC: .c=.o}의 수행을 살펴보면 make는 우선 ${SRC}를 evaluate하고 콜론의 앞에서 기술한 스트링을 찾아서 콜론 뒤의 스트링으로 치환한다.

주의할 점은 스트링 치환은 매크로의 끝이나 공백 앞에서만 일어난다. 또 콜론 뒤의 스트링이 널이 될 수도 있다.

예)

SRC = def.c redraw.c calc.c

FINAL_OBJS = ${SRC:.c=.o}

DEBUG_OBJ=$PSRC:.c=_dbg.o}

3.7 Prerequisite과 target을 위한 내부 매크로

make는 dependency line을 읽을 때마다 자신의 매크로를 정의한다. 이러한 매크로들은 description file을 간결하게 만들 수 있고 엔트리에 대한 변경을 용이하게 한다.

$@, $$@는 현재 target를 의미한다.(단 $$@는 dependency line에서만 사용 가능하다)

plot_prompt : basic.o prompt .o => plot_prompt : basic.o prompt .o

cc -o $@ basic.o prompt.o cc -o plot_prompt basic.o prompt.o

$?는 현재 target보다 newer인 prerequisite의 리스트를 의미한다

liobps : interact.o sche.o gen.o => liobps : interact.o sche.o gen.o

ar r $@ $? ar r liobps interact.o sche.o gen.o

4. 접미사 규칙(suffix rule)

UNIX system에서 C 언어의 소스화일은 .c, FORTRAN의 소스화일은 .f ASSEMBLY의 소스는 .s, 오브젝트 화일은 .o등의 접미사를 따른다. 이러한 규칙을 make에서 이용할 수가 있다.

4.1 접미사 규칙이란?

실제적으로 접미사 규칙이란 기정의된 일반화된 description file entry이다.

.SUFFIXES : .o .c .s # 사용하고자 하는 접미사의 기술

.c.o : # *.c에서 *.o를 생성하는 접미사 규칙

${CC} ${CFLAGS} -c $<

.s.o : # *.c에서 *.o를 생성하는 접미사 규칙

${AS} ${ASFLAGS} -o $@ $<

make가 사용할 접미사는 .SUFFIX를 이용하여 정의한다. .SUFFIX에 기술한 접미사를 포함하는 prerequisite에 대한 사용자가 제공하는 명시적인 명령어가 없는 경우에는 make는 접미사 규칙을 찾아 수행한다.

예) OBJS = main.o iodat.o dorun.o lo.o .SUFFIXES : .o .c .s

LIBS = /usr/proj/lib/crtn.a .c.o :

program : ${OBJS} ${LIB} ${CC} ${CFLAGS} -c $<

${CC} -o $@ ${OBJS} ${LIBS} .s.o :

main.o : main.c ${AS} ${ASFLAGS} -o $@ $<

${CC} -c $? OBJS = main.o iodat.o dorun.o lo.o

iodat.o : iodat.c LIBS = /usr/proj/lib/crtn.a

${CC} -c $? program : ${OBJS} ${LIB}

dorun.o : dorun.c ${CC} -o $@ ${OBJS} ${LIBS}

${CC} -c $?

lo.o : lo.s

${AS} -c $?


4.2 내부 매크로

$<: 접미사 규칙에서만 사용하는 내부 매크로로써 현재 target보다 newer한 prerequiste의

리스트를 나타낸다.

$* : 접미사 규칙에서 prerequisite의 접미사를 제외한 화일 이름 부분

예)

현재 접미사 규칙에서의 prerequiste 이 main.c라면

cp $< $*.tmp는 다음과 동일하다.

cp main.c main.tmp

4.3 Double colon

라이브러리를 구축하는 경우에는 서로 다른 화일에 서로 다른 명령어를 사용하여야 하는 경우가 발생한다. 일반적인 경우 make에서는 동일한 target이 여러 번 기술되고 그 기술마다 명령어가 연관되는 것은 허용되지 않는다. (동일한 target이 여러 번 기술되고 단 한번의 명령어 연관이 있는 경우에는 허용이 된다. 2.5절을 참조하라) 이러한 상황에서 double colon을 사용한 dependency line의 기술이 필요한 것이다. 다음의 예를 보자

예) libops :: interact.c

${CC} -O -c -DENTRY interact.c

ar r $@ interact.o

rm -f interact.o

libops :: sched.c

${CC} -O -c -DRECRD sched.c

ar r $@ sched.o

rm -f sched.o

prerequisite이 모두 libops보다 newer하다면 두개의 target기술의 명령어 행은 모두 실행이 된다.

4.4 Default값의 출력

macro나 suffix rule의 값을 살펴보면 좀더 description file을 명확하게 이해하고 수정할 수가 있을 것이다. make의 -p 옵션을 사용하면 suffix, macro의 값을 볼 수가 있다.

$make -p < dev/null >& make_defalut_values

는 description file이 널이므로 description file에 영향을 받지 않은 상태에서의 make의 suffix와 macro의 default 값을 볼 수 있다.

$make -pns >& make_default_values

-n : prevent make from executing commands

-s : prevent make from echoing the commands

는 기본 description file (Makefile또는 makefile, 다른 이름의 description file을 사용하고자 할 경우에는 -f 옵션을 사용하라)을 적용한 상황에서의 suffix와 macro의 값을 볼 수가 있다. 이는 description file의 디버깅 시에 유용하다.

4.5 Suffixes와 관련된 기타사항

Suffixes의 우선결합을 위하여 dependency chain의 앞쪽에 있는 화일들은 .SUFFIXES 리스트의 뒤쪽에 둔다. 이렇게 함으로써 불필요한 rebuild를 최소화할 수 있다.

다음과 같이 .SUFFIXES를 널로 하면 현재 사용하고 있는 모든 suffixes에 대한 것을 삭제한다.

.SUFFIXES : # delete all currently recognized suffixes

이를 이용하면 description file중간에서 .SUFFIXES를 재정의할 수가 있다.

.SUFFIXES : #delete all currently recognized suffixes

.SUFFIXES : .q .w .t #redefine suffixes list

5. 명령어(commands)

Description file내의 모든 명령어는 그 명령어들이 shell내에서 호출된 것처럼 실행이 된다. 그러므로 화일이름 확장(filename expansion)이나 출력 재지정(output redirection)같은 shell에서 제공하는 기능들을 description file내에서도 사용이 가능하다.

grep '^Error:' ../err{a-z}* | sort >> errorfile

그러나 description file내의 각각의 명령어들은 매번 독립적이 shell내에서 수행이 되므로 일반적인 shell script나 shell environment에 관련된 사항은 주의해서 사용하여야 한다.

5.1 화일이름 패턴 매칭

Shell의 화일이름 패턴 매칭 문자인 * ? [ ]는 명령어 라인과 dependency line의 콜론 우측에서 확장이 된다.

print : *.c

lpr *.c

date > $@

그러나 실제로 위와 같은 형태의 prerequisite은 make에서 제공하는 다수의 특정 prerequisite지정하는 방법으로 하는 것이 더욱 신뢰성이 있으므로 일반적으로 많이 사용하지는 않는다.

5.2 명령어 라인상에서의 newline의 영향

앞에서도 언급한 바와 같이 make는 각각의 명령어 라인을 개별적인 shell내에서 수행한다. 그러므로 다음과 같은 세가지 형태는 동일한 의미이다

터미널상의 입력 description file description file


cd output cd outputl; rm * cd output;\

rm * rm *


백 슬래시를 사용하여 newline을 command terminator로 해석하는 것을 중지시킴으로써 2줄 이상의 명령어를 한 줄의 명령어로 만들 수가 있다.

cd나 exit같은 쉘 내장 명령어를 사용하는 경우 그 효과는 그들이 위치한 곳에서만 영향을 미친다. 이는 각 명령어들이 각각의 독립적인 쉘에서 수행이 되기 때문이다.

마찬가지 이유로 쉘 변수에 붙이는 dollar($) sign은 두개를 붙여서 description file에서 사용하는 매크로 참조와 구분한다. make는 자동으로 두개의 dollar($) sign을 스트라이핑하여 쉘 변수로 인식한다. 여기서 사용되는 쉘 변수는 make내에서 생성된 쉘에서 사용하는 쉘 변수에 한정한다. 왜냐하면 환경 변수는 매크로와 마찬가지로 make script내에서 참조가 가능하기 때문이다. ( 3. 매크로의 사용을 참조하라.)

5.3 Error와 Exit Status

명령어 수행 시 에러가 발생하는 경우( non zero exit status)에 전체 make의 진행은 중지된다. make가 제공하는 이러한 기능은 대개의 경우 바람직한 특성이지만, 에러를 예견하고 에러 후에도 계속해서 진행을 하고자 할 때가 있다. make는 이런 경우를 위하여 다음과 같은 두 가지 방법을 제공한다.

첫째 description 화일내에서 명령어 앞에 하이픈(-)을 둠으로써 에러가 발생하여도 다음 명령어의 실행으로 진행할 수 있다.

plot :

cc -o $@ basic.c prompt.c

- cp $@ /usr/local # cp명령어 앞의 하이픈은 다음 명령어인 rm이

rm -f *.o # 항상 실행되게 보장한다.

두 번째 방법으로 .IGNORE라는 특별한 타겟을 description 화 일 내에 둠으로써 모든 명령어 앞에 하이픈을 붙인 것과 같은 효과를 얻을 수 있다.

한번에 독립적인 다수의 타겟을 만들고자 할 경우에는 make의 명령어 라인 옵션인 -k를 사용할 수가 있다.

make -k plot_prompt plot_win

이 옵션을 사용하면 다수의 타겟을 만드는 중에 독립적인 한 타겟에서 에러가 발생하는 경우 에러가 발생한 타겟과 그 타겟과 종속적인 prerequisite에 대한 모든 작업은 중지하고 독립적인 다른 타겟을 만드는 작업을 진행한다. make는 지정된 다수의 타겟 중 만들지 못한 대한 보고를 한다.

Error를 무시하지 않고 적절한 처리를 하고자 할 경우에는 쉘의 if문과 같은 것을 사용하여 적절한 처리를 할 수가 있다.

if \

cp plot /usr/local \

then \

rm -f plot \

else

echo 'Cannot move to new location

fi

5.4 쉘의 선택

대부분의 description 화일들은 bourn shell의 명령어를 포함하고 있다. 그러므로 표준적인 bourn shell을 사용함으로써 이식성을 최대화할 수 있고 적절한 프로그래밍 construct를 이용할 수가 있다.

description 화일이 이식성이 있도록 하기 위해서는 사용하는 특정 make implementation이나 login shell feature에 있는 기능은 되도록이면 사용하지 않는 것이 좋다. 그러므로 표준적인 bourn shell을 사용하여 프로그래밍하고 bourn shell이 사용됨을 명시적으로 description화일내에 다음과 같이 제일 처음에 기술하도록 한다

SHELL = /bin/bsh

부득이 하게 다른 쉘이나 perl 또는 awk하에서 수행하는 명령어를 사용해야 하는 경우에는 그러한 명령어를 별도의 화일로 분리하여 description 화일내에서 호출하는 것이 안전하다.











Appendix

make 사용법 요약

  1. 0 명령어 라인의 make 사용

make [ -f descfile ] [ options ] [ targets ] [ macro definition ]

options, targets, macro definition의 순서는 무관하며 여러 개의 옵션을 하나의 하이픈

으로 연결 가능하다.( 예를 들어 -fsn )

macro definition의 형식은 다음과 같다.

Name = string

표준 옵션

-b make의 이전 구현의 description 화일을 사용한다.

-d 디버깅 모드로 실행

내부 플래그와 화일의 최종 갱신 시각 등에 대한 정보를 제공

-e 환경 변수(environment variable)의 값을 description화일내의 매크로 정의

값보다 우선하여 사용한다.

-f Makefile이나 makefile이 아닌 다른 이름의 description화일을 사용함

화일 이름대신 - 를 사용하는 경우 stdin을 의미한다.

-i 에러가 발생하여도 무시하고 진행한다.

Description화일내에 .IGNORE와 동일하다.

-k 다수의 타겟을 만들 때 에러가 발생한 현재 타겟은 중지하고 다른 타겟

생성을 계속한다.

-n 명령어 라인을 실행하지는 않고 화면에 출력만 한다

-p 매크로 정의,suffixes, suffixes rule과 외부 description화일 엔트리를

출력한다

-q 타겟화일의 최근성 여부에 따라 zero또는 nonzero status를 리턴한다.

-r default rule을 사용하지 않는다.

-s 명령어라인을 echo하지 않는다.

Description 화일내의 .SILENT와 동일하다.

-t 명령어를 실행하지 않고 타겟화일을 touch한다

즉 타겟의 최종갱신시각을 현재시각으로 변경한다.

  1. 1 Description화일

#으로 시작하는 이후의 것은 주석(comment)이며 빈 라인은 무시한다. 백슬래시로 끝나는 라인은 다음 줄로 계속 이어진다. 백슬래시 전후의 모든 white space(blank,tab, new line)등은 한 개의 공백 문자로 해석된다.

  1. 1.0 Dependency line

형식

target : [:] [ prerequisites ] [; [ command ] ]

타겟을 만들기 위하여 필요한 prerequisite을 지정한다. 타겟들과 prerequisites은 공백이나 탭으로 분리하여야 한다, 콜론의 앞뒤에 공백은 선택적으로 둘 수도 있다. 타겟 앞에는 탭 문자가 와서는 안된다. 두개의 콜론을 사용하여 동일한 타겟을 다수 사용하여 다수의 명령을 각각의 dependency line에서 수행할 수가 있다. 세미콜론 후에 필요한 명령어를 기술할 수도 있다.

  1. 1.1 접미사규칙(Suffix Rule)

형식

.suffix[.suffix] :[ : ]

suffix만 다르고 동일한 이름을 가지는 두개의 화일에 대하여 두 번째 지정된 suffix를 가지는 화일은 첫번째 suffix를 가지는 화일에서 생성됨을 지정한다. suffixes간에는 공백 문자가 있으면 안된다. 콜론 뒤의 내용은 무시된다. 두개의 콜론은 dependency line과의 일관성을 위한 것일뿐 아무런 영향도 없다.

  1. 1.2 명령어(command)

형식

tab [- @] command

타겟을 생성하기 위하여 수행하는 명령어를 기술한다. 명령어는 반드시 탭으로 시작하여야 한다. 탭으로 시작한 이후에 추가적인 공백이나 탭은 허용된다. 명령어 앞에 하이픈(-)을 추가하여 명령어 실행 시 에러가 발생한 경우 에러를 무시하고 make의 수행을 계속하도록 한다. At-sign(@)을 명령어 앞에 추가하면 -n 명령어 옵션을 make수행 시 지정하지 않는 한 실행되는 명령어의 에코를 하지 않는다.

  1. 1.3 매크로 정의(macro definition)

형식

name = string

string을 name으로 할당한다. name의 앞에 추가적인 공백이 있을 수 있지만 명령어 라인과의 구분을 위하여 탭으로 시작하여서는 안된다. Equal sign(=)앞뒤에 공백 문자가 올 수 있지만 make에 의하여 제거된다. string이 백슬래시와 newline으로 이어지면 make는 다음 라인으로 string을 계속하여 인식한다. make는 백슬래시와 newline을 한 개의 공백 문자로 치환한다.

  1. 1.4 인클루드 문(include statement)

형식

include file

지정된 화일을 읽어 들여 그 화일의 내용을 description 화일로 해석한다.

A.2 매크로(macros)

다음은 make가 사용하는 내부적인 매크로에 대한 설명이다.

$? 현재 타겟을 만들기 위한 prerequisite중에서 현재 타겟의 최종 갱신 시각보다

이전의 갱신 시각을 가진 prerequisite의 리스트

Suffix rule에서는 사용할 수 없음

$@ 현재 생성하려 하는 타겟

$< 현재 타겟을 만들기 위한 prerequisite중에서 현재 타겟의 최종 갱신 시각보다

이전의 갱신 시각을 가진 prerequisite

Suffix rule과 .DEFAULT에서만 사용 가능

$* 현재 타겟을 만들기 위한 prerequisite중에서 현재 타겟의 최종 갱신 시각보다

이전의 갱신 시각을 가진 prerequisite의 suffix를 제외한 이름

Suffix rule에서만 사용 가능

$$@ Dependency line의 콜론 우측에서만 사용이 가능하며 현재 타겟의 이름이다.

$% 현재 타겟이 라이브러리 모듈일 경우에 대응하는 .o 화일의 이름이다

D $?를 제외한 내부 매크로의 디렉토리 부분이다.

${*D}, ${<D}, ${@D}, $${@D}로 사용한다.

F $?를 제외한 내부 매크로의 화일부분이다.

${*F}, ${<F}, ${@F}, $${@F}로 사용한다.

A.3 매크로 스트링 치환

형식

${macro:s1=s2}

${macro}의 정의 중에서 공백이나 탭의 바로 앞뒤 또는 매크로 정의의 끝에 있는 s1을 s2로 치환한다.

A.4 특별한 매크로

SHELL

명령어를 해석하기 위한 쉘을 지정한다. 이 매크로가 지정이 되지 않은 경우 명령

어 해석을 위하여 사용되는 쉘은 make구현에 따라 차이가 있다.

VPATH

prerequisite이 현재 디렉토리 이외의 곳에 있는 경우에 그 디렉토리를 지정하는데

사용된다.

A.5 특별한 타겟이름

.DEFAULT

생성하고자 하는 타겟과 관련이 있는 suffix rule이나 description화일 엔트리가 없

는 경우에 타겟과 연관된 명령어만 수행한다.

.IGNORE

에러 코드를 무시한다. -i옵션과 동일하다.

..PRECIOUS

make를 kill하거나 명령어가 에러를 발생하여 make가 abort되더라도 이 타겟에서

지정한 화일을 제거되지 않는다.

.SILENT

명령어의 수행을 하지만 그 과정을 에코하지 않는다. -s 옵션과 동일하다

.SUFFIXES:

make의 suffix rule에서 사용할 suffix의 리스트를 기술한다. suffix의 리스트를 기술

하지 않으면 기존의 .SUFFIXES의 값을 널이 된다.






실질적인 컴파일 이전에 미리 처리되는 문장으로 선행처리기라고도 한다.
컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 작업들을 먼저 수행한다.

종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다.
이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화와 비활성화하는 데에 가장 많이 이용된다.
즉, 기존에 있는 소스 코드를 건드리지 않고 부분적인 컴파일을 하는 것이다.

C의 전처리문이 오는 줄(Line)의 첫 문자는 항상 '#'으로 시작한다.

ANSI 표준에 따른 C의 전처리문의 종류
- 파일 처리를 위한 전처리문 : #include
- 형태 정의를 위한 전처리문 : #define, #undef
- 조건 처리를 위한 전처리문 : #if, #ifdef, #ifndef, #else, #elif, #endif
- 에러 처리를 위한 전처리문 : #error
- 디버깅을 위한 전처리문 : #line
- 컴파일 옵션 처리를 위한 전처리문 : #pragma

조건 처리를 위한 전처리문은 어떤 조건에 대한 검사를 하고 그 결과를 참(0 이 아닌 값) 또는 거짓(0)으로 돌려준다.
#if : ...이 참이라면
#ifdef : ...이 정의되어 있다면
#else : #if나 #ifdef에 대응된다.
#elif : 'else + if'의 의미
#endif : #if, #ifdef, #infdef이 끝났음을 알린다.

#include
헤더 파일과 같은 외부 파일을 읽어서 포함시키고자 할 때 사용된다. 이때의 파일은 이진 파일(Binary file)이 아닌 C의 소스 파일과 같은 형태의 일반 문서 파일을 말한다:
#include <stdio.h>        /* 이 위치에 stdio.h라는 파일을 포함시킨다. */
#include "text.h"           /* 이 위치에 text.h라는 파일을 포함시킨다. */

<...>을 사용할 때와 ...을 사용할 때의 차이점은 <...>은 컴파일러의 표준 포함 파일 디렉토리(또는 사용자가 별도로 지정해 준)에서 파일을 찾는 것을 기본으로 한다. 그리고 ...을 사용했을 때는 현재의 디렉토리를 기본으로 파일을 찾게 된다. 아예 디렉토리를 같이 지정할 수도 있다:
#include <C:\MYDIR\MYHEAD.H>
#include "C:\MYDIR\MYHEAD.H"

#define
상수 값을 지정하기 위한 예약어로 구문의 상수로 치환한다. 또한 #define은 함수 역활과 비슷하게 아래와 같이 쓰일 수 있다:
#define SUM(x) ((x) = (x) + (x))

#define으로 정의할 수 있는 것은 숫자만이 아니다:
#define MYNAME "Young Hee"

이렇게 #define으로 정의된 것은 일반적인 변수와는 다르다. 그 차이는 명백하다:
#define MYNAME "Turbo"
char my_name[] = "Turbo"

MYNAME은 전처리문으로 my_name은 문자형 배열 변수로 정의되었다:
printf(MYNAME);
printf(MYNAME);
printf(my_name);
printf(my_name);

이것을 전처리한 상태는 다음과 같이 될 것이다:
printf("Turbo");
printf("Turbo");
printf(my_name);
printf(my_name);

이런 결과에서 우리가 유추해 볼 수 있는 것은 전처리 명령을 사용했을 경우 "Turbo"라는 동일한 동작에 대해서 두개의 똑같은 문자열이 사용됐고, 변수를 사용했을 경우에는 하나의 문자열을 가지고 두번을 사용하고 있다는 것이다. 결과적으로 이런 경우에는 전처리문을 사용했을 경우 메모리 낭비를 가져 온다는 것을 알 수 있다.

#undef
#define으로 이미 정의된 매크로를 무효화한다:
#define ADD(a, b) (a + b)
#undef ADD(a, b)

앞으로 사용되는 ADD(...)는 undefined symbol이 되어 에러 처리된다.

#if ~ #endif
#if 구문은 if랑 아주 비슷하다. 이것은 어떠한 구문을 컴파일 할지 안할지를 지정할 수 있다:
#define A 1
#if A
source code ...
#endif

위 source code 부분은 컴파일이 된다. if문에서와 같이 참, 거짓을 구분하여 컴파일이 된다. 위에서 A값은 1 즉 0보다 큰 수이기 때문에 참인 것이다. 직접 아래와 같이 하면 거짓이기 때문에 source code 부분은 컴파일이 되지 않는다:
#if 0
source code ...
#endif

#ifdef ~ #endif
컴파일 할 때
#define MYDEF                /* MYDEF는 값은 가지지 않았지만 어쨋든 정의는 되었다 */

#ifdef YOURDEF              /* 만약 YOURDEF가 정의되어 있다면... */
#define BASE 10             /* BASE == 10 */
#elif MYDEF                    /* 그외에 MYDEF가 정의되었다면... */
#define BASE 2               /* BASE == 2 */
#endif

BASE는 상수 2로 치환되어 전처리기가 컴파일러에게 넘겨준다.

#ifndef 헤더명_H__ ~ #endif
헤더 파일이 겹치는 것을 막기 위한 일종의 매크로이다. 예를 들어, 헤더 파일에 어떤 클래스의 인터페이스 선언을 넣었다고 하자. 이 클래스 인터페이스에서 다른 파일의 프로토타입이 필요해서 다른 A 파일을 include 하고 있는데 이 헤더 파일을 include 하는 파일에서 A라는 헤더 파일을 이미 include 하고 있다면 두번 define한 것이 된다. 그러면 SYNTEX 에러가 난다. 그래서 그런 것을 막는 방법의 하나로 #ifndef을 사용한다. 이전에 include되어 있으면 #endif 쪽으로 점프해 버려 결국 한번 선언되는 것이다:
#include  <stdio.h>    ------ (a)
#include  <stdio.h>    ------ (b)

이렇게 두번 썼다고 하자. 그런데 앞에 이미 include를 했는데 밑에 또 한다면 문제가 된다. 컴파일러가 검사해야할 코드량도 많아진다. 그래서 stdio.h에는
#ifndef STDIO_H__
#define STDIO_H__
가 선언되어 있다. 만약 STDIO_H가 선언되어 있지 않다면 선언한다는 뜻이다. 그 뒤 (b)에서는 이미 (a)쪽에서 STDIO_H__ 을 선언한 상태이기 때문에 전처리기 쪽에서 무시해버린다. 그러므로 컴파일러는 (a)만 검사한다.

#defined
define이 여러 개 되어 있는지를 검사할 때 쓴다. 이것은 여러 개를 동시에 검사 할 수 있다:
#if (defined A) || (defined B)

#ifdef와 #if defined의 차이
#ifdef은 정의가 되어 있는지를 테스트 하기 때문에, 한번에 여러 개를 사용할 수 없다:
#ifdef name

여러 개가 정의되어 있는지를 테스트 하기 위해서 #if defined를 사용할 수 있다:
#if defined(MACRO1) || defined(MACRO2)

#if는 ||로 중첩해서 사용할 수 있다. 형식이, #if expression이므로 C 표현이 올 수 있다:
#if (MACRO1) || (MACRO2)

#error
소스 라인에 직접 에러 메세지를 출력한다. 전처리기가 #error 문을 만나면 그 즉시 컴파일을 중단하고 다음과 같은 에러 메시지를 출력한다:
ERROR : XXXXX.c ########: Error directive: 내용
- XXXXX.c --> 현재 컴파일 중인 파일 명
- ####### --> 전처리기가 #error 문을 만난 시점에서의 행 번호(헤더 포함)

#ifdef __LARGE__
#error This program must be compiled in LARGE memory model!
#endif

이 내용은 만일 프로그램이 LARGE 모델이 아니라면 "#error" 뒤에 표시된 메세지를 출력하고 컴파일을 중지하게 된다.

#line
이 명령은 소스 코드의 행 번호를 지정하기 위한 것으로 주로 컴파일러에 의해 미리 정의된 __LINE__과 함께 사용된다.
__LINE__과 __FILE__을 각각 행 번호와 파일 명으로 변경한다:
#include <stdio.h>
#define DEBUG

void main(void)
{
        int count = 100;

        #line 100               /* 다음 줄번호를 100으로 설정한다 */
                                   /* <-- 이 줄의 번호가 100이다 */
        #ifdef DEBUG        /* <-- 이 줄의 번호가 101이다 */
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif

        count = count * count - 56;
        #ifdef DEBUG
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif
        count = count / 2 + 48;
        #ifdef DEBUG
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif
}

#pragma
컴파일 옵션의 지정. 컴파일러 작성자에 의해서 정의된 다양한 명령을 컴파일러에게 제공하기 위해 사용되는 지시어이다. 컴파일러의 여러 가지 옵션을 명령행에서가 아닌 코드에서 직접 설정한다. #pragma는 함수의 바로 앞에 오며 그 함수에만 영향을 준다.
Turbo C는 9개의 #pragma 문(warn, inline, saveregs, exit, argsused, hdrfile, hdrstop, option, startup)을 지원하고 있다:

#pragma inline
컴파일할 때 어셈블러를 통해서 하도록 지시한다. 즉, 인라인 어셈블리 코드가 프로그램에 있음을 알려준다(명령행 상에서 '-B' 옵션).

#pragma saveregs
이 홉션은 휴즈 메모리 모델에 대해 컴파일된 함수에게 모든 레지스터를 저장하도록 한다.

#pragma warn
이 지시어는 Turbo C에게 경고 메시지 옵션을 무시하도록 한다.

#pragma warn -par
이는 매개 변수(parAMETER)가 사용되지 않았다고 경고(warnING)를 내지 못하도록 한다. 이와 반대되는 표현은
#pragma warn +par

경고의 내용 앞에 (+)를 사용하면 경고를 낼 수 있도록 설정하고 (-)를 사용하면 경고를 내지 못하도록 하는 것은 모든 경고에 대해 동일하다. 명령 행에서는 "-wxxx"로 경고를 설정하고 "-w-xxx"로 경고를 해제한다. 경고의 종류는 무척 많은데 자주 사용되는 것을 아래에 나타냈다. 모든 것을 알고 싶다면 컴파일러 User's Guide의 명령행 컴파일러 부분을 참고하기 바란다:
par : 전해진 파라미터가 사용되지 않음
rvl : void 형이 아닌 함수에서 리턴 값이 없음
aus : 변수에 값을 할당했으나 사용하지 않았음
voi : void 형 함수에서 리턴 값이 사용되었음
sig : 부호 비트(MSB)가 고려되지 않은 형 변환(type-castion)에서 부호 비트를 소실할 수 있음

Standard C pre-defined symbols
__FILE__ a string that holds the path/name of the compiled file
__LINE__ an integer that holds the number of the current line number
__DATE__ a string(Mmm dd yyyy) that holds the current system date
__TIME__ a string(hh:mm:ss) that holds the current system time
__STDC__ defined as the value '1' if the compiler conforms with the ANSI C standard
__cplusplus determines if your compiler is in C or C++ mode. Usually used in headers

#include <stdio.h>

void main(void)
{  
        printf("The path/name of this file is %s\n", __FILE__);  
        printf("The current line is %d\n", __LINE__);  
        printf("The current system date is %s\n", __DATE__);  
        printf("The current system time is %s\n", __TIME__);  

        #ifdef __STDC__  
        printf("The compiler conforms with the ANSI C standard\n");  
        #else  
        printf("The compiler doesn't conform with the ANSI C standard\n");  
        #endif  

        #ifdef __cplusplus  
        printf("The compiler is working with C++\n");  
        #else  
        printf("The compiler is working with C\n");  
        #endif 


프로그래머들 마다 코딩 스타일(암시적 약속)이 있다. 보통 매크로, const 변수는 대문자로 적는 것이 원칙이다. 매크로 함수와 일반 함수, 매크로 대상체(object-like macro)와 일반 변수를 구분하기 쉽게 해주는 것이기 때문이다.

#define STDIO_H_
왜 뒤에 _를 붙였을까? 이것도 하나의 암시적 약속이다. 컴파일러 제작 회사는 매크로를 정의할 때 사용자들과 이름이 충돌이 나지 않게 하기 위해서 대부분 _를 뒤어 덧붙인다. 또한 _를 하나 혹은 두 개 연속으로 시작하는 것은 컴파일러 내부에서 사용하는 매크로라는 성격이 강하다. 물론 강제적인 뜻은 없으며 단지 관습상 그렇다. 왜 이것이 관습이 되었나 하면 보통 매크로 변수 이름이나 함수 이름을 지을 때 뒤에 _를 붙이지 않기 때문이다. 그래서 함수 제작자들이 _를 단골로 붙였다
make와 같은 ANT라는 컴파일러가 있는것 같다..물론 좀더 봐야 뭔지 알겠지만...
트랙백으로 좀 확인해 봐야 겠다.
이 내용은 하기 내용에서 trackback 해왔다.
ANT가 Make보다 좋은 점은 각종 서적과 문서에 나와 있으므로 패스.

우선, CVS에서 프로젝트를 checkout

[root@fedora cvs]# cvs checkout hibernate-start01
cvs checkout: Updating hibernate-start01
cvs checkout: Updating hibernate-start01/WebContent
U hibernate-start01/WebContent/test.jsp
cvs checkout: Updating hibernate-start01/WebContent/WEB-INF
U hibernate-start01/WebContent/WEB-INF/web.xml
cvs checkout: Updating hibernate-start01/WebContent/WEB-INF/classes
U hibernate-start01/WebContent/WEB-INF/classes/hibernate.cfg.xml
cvs checkout: Updating hibernate-start01/WebContent/WEB-INF/lib
U hibernate-start01/WebContent/WEB-INF/lib/antlr-2.7.6.jar
U hibernate-start01/WebContent/WEB-INF/lib/asm-attrs.jar
U hibernate-start01/WebContent/WEB-INF/lib/asm.jar
U hibernate-start01/WebContent/WEB-INF/lib/c3p0-0.9.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/cglib-2.1.3.jar
U hibernate-start01/WebContent/WEB-INF/lib/commons-collections-3.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/commons-logging-1.0.4.jar
U hibernate-start01/WebContent/WEB-INF/lib/dom4j-1.6.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/ehcache-1.2.3.jar
U hibernate-start01/WebContent/WEB-INF/lib/hibernate3.jar
U hibernate-start01/WebContent/WEB-INF/lib/javassist-3.4.GA.jar
U hibernate-start01/WebContent/WEB-INF/lib/jta-1.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/log4j-1.2.11.jar
U hibernate-start01/WebContent/WEB-INF/lib/servlet-api.jar
U hibernate-start01/WebContent/WEB-INF/lib/slf4j-api-1.5.6.jar
U hibernate-start01/WebContent/WEB-INF/lib/slf4j-jdk14-1.5.6.jar
cvs checkout: Updating hibernate-start01/resources
U hibernate-start01/resources/jdbc.properties
cvs checkout: Updating hibernate-start01/src
cvs checkout: Updating hibernate-start01/src/com
cvs checkout: Updating hibernate-start01/src/com/greatshin
cvs checkout: Updating hibernate-start01/src/com/greatshin/hibernatestart
U hibernate-start01/src/com/greatshin/hibernatestart/Event.hbm.xml
U hibernate-start01/src/com/greatshin/hibernatestart/Event.java
U hibernate-start01/src/com/greatshin/hibernatestart/EventManager.java
U hibernate-start01/src/com/greatshin/hibernatestart/EventServlet.java
U hibernate-start01/src/com/greatshin/hibernatestart/HibernateUtil.java
U hibernate-start01/src/com/greatshin/hibernatestart/Person.hbm.xml
U hibernate-start01/src/com/greatshin/hibernatestart/Person.java
U hibernate-start01/src/com/greatshin/hibernatestart/User.hbm.xml
U hibernate-start01/src/com/greatshin/hibernatestart/User.java
U hibernate-start01/src/com/greatshin/hibernatestart/UserManager.java
U hibernate-start01/src/com/greatshin/hibernatestart/UserServlet.java
[root@fedora cvs]# ls -al


방금 checkout한 hibernate-start01 프로젝트를 빌드해보자.

ANT는 빌드정보를 XML파일로 관리한다.

아래와 같이 작성하자.

build.xml의 속성 엘리먼트는 매뉴얼을 참조하자.

<?xml version="1.0"?>
<project name="spring-template-web" basedir="." default="start">
 <property file="build.properties" /> 

 <property environment="env" />
 <property name="tomcat.home" value="${env.CATALINA_HOME}" />
 <property name="deploy.dir" value="${tomcat.home}/webapps/${webapp.name}" />

 <path id="classpath">
  <fileset dir="${lib.dir}" includes="*.jar" />
  <pathelement path="${build.dir}" />
 </path>

 <target name="compile" description="Compile main source tree java files">
  <mkdir dir="${build.dir}/classes" />

  <javac destdir="${build.dir}/classes" debug="true" optimize="false" deprecation="false" failonerror="true">
   <src path="${src.dir}/com" />
   <classpath refid="classpath" />
  </javac>

  <!-- Copy XML files to ${build.dir}/classes -->
  <copy todir="${build.dir}/classes">
   <fileset dir="${src.dir}" includes="**/*.xml" />
  </copy>

  <native2ascii src="${resources.dir}" dest="${build.dir}/classes" includes="**/*.properties" />
 </target>

 <patternset id="war.files">
  <include name="**/*.*" />
  <exclude name="**/cargo*.jar" />
  <exclude name="**/junit.jar" />
  <exclude name="**/jwebunit*.jar" />
  <exclude name="**/httpunit*.jar" />
  <exclude name="**/*mock*.jar" />
  <exclude name="**/servlet-api*.jar" />
  <exclude name="**/strutstestcase*.jar" />
  <exclude name="**/Tidy.jar" />
 </patternset>

 <target name="war" depends="compile" description="Packages app as WAR">
  <mkdir dir="${dist.dir}" />
  <lib dir="${common.lib.dir}">
   <exclude name="**/servlet-api.jar" />
   <exclude name="**/junit.jar" />
  </lib>
  <war destfile="${dist.dir}/${webapp.name}.war" webxml="${web.dir}/WEB-INF/web.xml" compress="true">
   <classes dir="${build.dir}/classes" />
   <fileset dir="${web.dir}">
    <patternset refid="war.files" />
    <exclude name="**/web.xml" />
   </fileset>
  </war>
 </target>

 <target name="deploy" depends="compile" description="Deploy application">
  <copy todir="${deploy.dir}" preservelastmodified="true">
   <fileset dir="${web.dir}">
    <patternset refid="war.files" />
   </fileset>
  </copy>
  <copy todir="${deploy.dir}/WEB-INF/classes" preservelastmodified="true">
   <fileset dir="${build.dir}/classes" />
  </copy>
  <copy todir="${deploy.dir}/WEB-INF/lib" preservelastmodified="true">
   <fileset dir="${lib.dir}">
    <exclude name="**/junit.jar" />
    <exclude name="**/servlet-api.jar" />
   </fileset>
  </copy>
 </target>

 <target name="deploywar" depends="war" description="Deploy application as a WAR file">
  <copy todir="${deploy.dir}" preservelastmodified="true" file="${dist.dir}/${webapp.name}.war" />
 </target>

 <target name="clean" description="Clean output directories">
  <delete dir="build" />
  <delete dir="${dist.dir}" />
 </target>

 <!-- Tomcat Ant Tasks -->
 <taskdef file="tomcatTasks.properties">
  <classpath>
   <pathelement path="${tomcat.home}/server/lib/catalina-ant.jar" />
  </classpath>
 </taskdef>

 <target name="remove" description="Remove application from Tomcat">
  <undeploy url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="reload" description="Reload application in Tomcat">
  <reload url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="start" depends="stop" description="Start Tomcat application">
  <start url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="stop" depends="deploy" description="Stop Tomcat application">
  <stop url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="list" description="List Tomcat applications">
  <list url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" />
 </target>

</project>

build.xml 파일 작성이 완료되었으면 아래와 같이 빌드한다.

위 설정파일에는 컴파일 후 배포까지 포함되어 있음을 참조하자.

[root@fedora hibernate-start01]# ant
Buildfile: build.xml
compile:
    [mkdir] Created dir: /home/cvs/hibernate-start01/build/classes
    [javac] Compiling 8 source files to /home/cvs/hibernate-start01/build/classes
     [copy] Copying 3 files to /home/cvs/hibernate-start01/build/classes
[native2ascii] Converting 1 file from /home/cvs/hibernate-start01/resources to /home/cvs/hibernate-start01/build/classes
deploy:
     [copy] Copying 12 files to /usr/local/tomcat/webapps/hibernate-start01/WEB-INF/classes
stop:
     [stop] OK - Stopped application at context path /hibernate-start01
start:
    [start] OK - Started application at context path /hibernate

+ Recent posts