'Software Architect/LINUX'에 해당되는 글 10건

Software Architect/LINUX

  이전 시간에는 간단히 컴파일하는 방법을 소개해 드리면서 프로그램 모듈화의 필요성을 말씀을 드렷습니다. 이번 시간에는 Makefile에 대해 소개해 드리겠습니다. Makefile을 사용하는 이유는 편리하기 때문입니다. Makefile을 이용하지 않아도 컴파일할 수 있기 때문이죠. 어떻게 편리한지 보겠습니다.

간단한 Makefile 만들기

  설명을 위해 아래와 같이 6개의 소스 파일이 있다고 하겠습니다.

  • main.c와 main.h
  • tcp.c와 tcp.h
  • rs232.c와 rs232.h

  여기서 생각해야 될 점은 main.c를 언제 컴파일하느냐 하는 것입니다. main.c 는 rs232.c의 함수와 tcp.c의 함수를 사용하기 때문에 main.c 자체가 변경되는 것 외에도 rs232.h와 tcp.h가 변할 때에도 컴파일하는 것이 안전합니다.

  • main.c 자체가 수정되었을 경우
  • main.h 가 수정되었을 경우
  • rs232.h 또는 tcp.h 가 변했을 경우

  tcp.c와 rs232.c는 자기 자신이 바뀌거나 해당 헤더파일이 변경되면 다시 컴파일이 되도록 합니다. 이제 이를 위한 간단한 Makefile을 보시겠습니다.

간단한 Makefile 만들기 

  Makefile 의 구조는 아래와 같습니다.

목표: 아래 명령을 실행하게 되는 모든 조건에 해당되는 파일 목록
    실행 명령어

  이와 같은 구조로 필요한 만큼 나열하면 됩니다. 다음은 실제 Makefile 내용입니다.

 주의

명령어 앞에는 반드시 탭 문자로 간격을 띄워야 합니다. 
공백으로 띄우시면 make 실행 시 에러가 발생합니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o sample main.o tcp.o rs232.o  <-- 명령어, 즉 gcc 앞에는 tab 키로 들여 쓰기를 해야합니다. 

main.o : main.c main.h rs232.h tcp.h 
    gcc -c main.c       <-- sample 에서 링크할 것이므로 여기에서는 -c를 사용하여 컴파일만 하겠습니다. 

tcp.o : tcp.c tcp.h 
    gcc -c tcp.c

rs232.o : rs232.c rs232.h 
   gcc -c rs232.c

sample : main.o tcp.o rs232.omain.o tcp.o, rs232.o 파일 중 변경되는 파일이 있다면
아래의 명령을 실행합니다.
     gcc -lm -o sample main.o tcp.o rs232.omain.o, tcp.o, rs232.o로 실행파일 sample 를 만듭니다.
main.o : main.c main.h rs232.h tcp.h

main.c, main.h, rs232.h tcp.h 파일 중 변경되는 파일이
있다면 아래의 명령을 실행합니다.

     gcc -c main.cmain.c 를 컴파일해서 main.o를 생성합니다.
tcp.o : tcp.c tcp.htcp.c, tcp.h 파일 중 변경되는 파일이 있다면
아래의 명령을 실행합니다.
     gcc -c tcp.ctcp.c 를 컴파일해서 tcp.o를 생성합니다.
rs232.o : rs232.c rs232.hrs232.c, rs232.h 파일 중 변경되는 파일이 있다면 
아래의 명령을 실행합니다.
     gcc -c rs232.crs232.c 를 컴파일해서 rs232.o를 생성합니다.

  대충 이해가 되시나요? 뭐야 이거? 하시는 분이 계실지 모르겠습니다. 위이 예제는 아주 무식하지만 직관적으로 바로 이해되는 아주 간단한 Makefile 예제가 되겠습니다.

 

이제부터는 make 로 컴파일 완료

  이제 쉘에서 make 라는 명령으로 간단히 컴파일하고 실행 파일을 만들 수 있습니다.

]$ make

   편하죠? 그런데 Makefile 을 이렇게 만들면 타이핑을 해야할 것이 너무 많지요. 대부분의 프로그래머는 아주 게으릅니다. 타이핑으로 먹고 사는 사람들이 타이핑 많은 거, 좋와하지 않죠.

  이제, Makefile 만의 매크로를 이용하여 타이핑하는 횟수를 줄여 보겠습니다.

 

$@ $* $< $?

$@목표 이름
$*목표 이름에서 확장자가 없는 이름
$<

조건 파일 중 첫번째 파일

$?목표 파일 보다 더 최근에 갱신된 파일 이름

   자, 소개한 매크로를 이용하여 Makefile의 내용을 좀더 간단하게 만들 수 있습니다. 링크하는 부분을 먼저 보겠습니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o sample main.o tcp.o rs232.o

  여기서 $@는 무엇일까요? 네, sample이 되겠습니다. 그러므로 아래와 같이 수정할 수 있습니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o $@ main.o tcp.o rs232.o

  이해 되시죠. 이 번에는 $*오 $<, $? 에 대해서 말씀드리겠습니다.

tcp.o : tcp.c tcp.h 
    gcc -c tcp.c

  $*는 목표 이름에서 확장자를 제거한 이름이니까 .o를 뺀 tcp가 되겠습니다. 그러므로 $*를 이용하면 이렇게 수정할 수 있습니다

tcp.o : tcp.c tcp.h
    gcc -c $*.c

  이해 되시죠? $<는 조건에 열거된 파일 목록 중 첫번재를 의미합니다. 그러므로 $<는 조건 파일인 tcp.c tcp.h 중에서 첫번째 파일의 이름에 해당되므로 tcp.c 가 되겠습니다. 그러므로 아래와 같이 바꿀 수도 있습니다. 저 같은 경우 제일 많이 사용합니다.

tcp.o : tcp.c tcp.h 
    gcc -c $<

  $? 는 "현재 목표 파일 파일 보다 더 최근에 갱신된 파일 이름"을 나타내는 매크로입니다. 뜻은 알겠는데, 도대체 어디에 사용하는지 필요성을 잘 모르겠네요. 여하튼 예로 따져 보변 tcp.o 후에 tcp.h 가 수정되었다면 tcp.h가 된다는 것입니다.

 

확장자 규칙 .c.o

  일반적으로 .o 는 .c 로 만들어 지므로 실은 위의 예를 아래와 같이 명령 실행 없이 작성해도 make가 실행이 됩니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o $@ main.o tcp.o rs232.o

main.o : main.c main.h rs232.h tcp.h-> 명령 실행이 없다.
tcp.o : tcp.c tcp.h-> 명령 실행이 없다.
rs232.o : rs232.c rs232.h-> 명령 실행이 없다.

  에이~ 그런데 왜 처음부터 복작하게 썼어? 하시겠지만, 이는 $*, $<, $? 를 설명 드리기 위함도 있지만 위와 같이 처리하면 컴파일은 되지만 컴파일에 대한 상세한 옵션을 처리할 수가 없습니다. 위와 같이 Makefile을 작성하고 make 를 실행하면 아마도 아래와 같이 단순한 모습으로 컴파일 될 것입니다.

]$make
cc -c -o main.o mainc
cc -c -o tcp.o tcp.c
cc -c -o rs232.o rs232.c

  요렇게 말이죠. 그러나 컴파일 할 때에는 인클루드 경로명을 지정하는 것과 같은 옵션을 사용해야 합니다. 이렇게 자동으로 진행되는 것만을 의지할 수 없습니다. 그래서 아래와 같이 .o 를 어떻게 만들어 낼지를 make 에 알려 줍니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o $@ main.o tcp.o rs232.o

.c.o:
    gcc -I/home/jwjw/prjs/include -g -c $<

main.o : main.c main.h rs232.h tcp.h
tcp.o : tcp.c tcp.h
rs232.o : rs232.c rs232.h

  이제 make를 실행하면 아래와 같이 컴파일되는 모습을 보실 수 있습니다.

gcc -I/home/jwjw/prjs/include -g -c main.c
gcc -I/home/jwjw/prjs/include -g -c tcp.c
gcc -I/home/jwjw/prjs/include -g -c rs232.c
gcc -lm -o sample main.o tcp.o rs232.o

  또한 소스 파일이 Makefile과 같은 폴더 안에 있다면 아예 아래와 같이 작성하셔도 컴파일이 됩니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o $@ main.o tcp.o rs232.o
.c.o:
    gcc -I/home/jwjw/prjs/include -g -c $<

  그러나 문제는 각 소스에 대한 컴파일 조건이 매우 단순해 지게 됩니다. 즉, main.c 는 main.c 자신이 수정될 때만 컴파일이 됩니다. 위의 예에서 처럼 rs232.h가 수정되거나 아예 관계가 아주 깊은 main.h 가 수정되더라도 main.c 는 재 컴파일이 안됩니다. 소스끼리 관계가 있다며 하단 부분을 서술해 주셔야 합니다.

sample : main.o tcp.o rs232.o
    gcc -lm -o $@ main.o tcp.o rs232.o
.c.o:
    gcc -I/home/jwjw/prjs/include -g -c $<

main.o: main.c main.h rs232.h
rs232.o: rs232.c rs232.h
tcp.o: tcp.c tcp.h

  그래도 많이 줄어 들었죠? 그러나 이게 다가 아닙니다.

 

매크로로 치환

  예제의 main.o tcp.o rs232.o 의 파일 이름이 2번 중복되어 있습니다. 두번 타이핑을 해야 하는데, 앞서 말씀드렸듯이 프로그래머는 게으릅니다. 2번~? 귀찮습니다. 아래와 같이 수정해 봅시다.

OBJS = main.o tcp.o rs232.o

sample : $(OBJS)
    gcc -lm -o $@ $(OBJS)
.c.o:
    gcc -I/home/jwjw/prjs/include -g -c $<

 또는 $^ 를 이용하여 아래와 같이 수정할 수 도 있습니다. $^는 조건에 있는 모든 파일 이름을 대신하는 매크로입니다.

OBJS = main.o tcp.o rs232.o

sample : $(OBJS)
    gcc -lm -o $@ $^
.c.o:
    gcc -I/home/jwjw/prjs/include -g -c $<

 하는 김에 컴파일 옵션과 링크 옵션도 매크로로 치환해 보겠습니다.


OBJS = main.o tcp.o rs232.o
CC = -I/home/jwjw/prjs/include -g -c

sample : $(OBJS)
    gcc -lm -o $@ $^
.c.o:
    gcc $(CC) $<

 이렇게 매크로로 치환하여 Makefile 의 윗 행에 모아 두면, 내용 전체를 볼 필요 없이 매크로 부분만 보거나 수정해도 되기 때문에 편리합니다. 이래서 아래와 같이 수정하여 완성할 수 있습니다.

TARGET = sample
OBJS = main.o tcp.o rs232.o
CC = -I/home/jwjw/prjs/include -g -c

$(TARGET : $(OBJS)
    gcc -lm -o $@ $^
.c.o:
    gcc $(CC) $<

main.o: main.c main.h rs232.h
rs232.o: rs232.c rs232.h
tcp.o: tcp.c tcp.h

gccmakedep

  다른 것들은 모두 편리하고 좋은 것 같은데, Makefile 하단에 있는 파일 간의 종속에 대한 정보를 모두 타이핑해서 넣어야 할까요? 파일이 많을 경우 어떻게 일일이 입력할 수 있있겠습니까?  당연한 말씀입니다. 게으른 프로그래머에게는 말도 안되죠. 그래서 이와 같은 귀찮은 작업을 make에 떠 넘기겠습니다. 바로 파일간의 의존성을 찾아서 그 내용을 직접 구성해 달라고 요청하는 것이죠.

  이렇게 파일의 의존성을 검색해서 그 내용을 작성해 주는 것이 gccmakedep 입니다. 아래와 같이 수정해서 make dep를 실행합니다.

TARGET = sample
OBJS = main.o tcp.o rs232.o
SRCS = $(OBJS:.o=.c)
CC = -I/home/jwjw/prjs/include -g -c

$(TARGET): $(OBJS)
    gcc -lm -o $@ $^

.c.o:
    gcc $(CC) $<

dep : 
    gccmakedep $(SRCS)

  이렇게 추가 작성해서 make dep 를 실행하시면 make는 컴파일과 링크 작업 대신에 라벨 dep: 밑의 명령을 실행합니다. 새로 만들어진 SRCS는 OBJS에 열거된 파일 모록에 대해서 확장자를 .o를 .c로 바뀐 목록을 가지게 됩니다. gccmakedep는 소스 파일을 가지고 의존성을 검색할 수 있기 때문이죠.

]$ make dep
]$ vi Makefile

TARGET = sample 
OBJS = main.o tcp.o rs232.o 
SRCS = $(OBJS:.o=.c) 
CC = -I/home/jwjw/prjs/include -g -c 
    $(TARGET): $(OBJS) gcc -lm -o $@ $^ 
.c.o: 
    gcc $(CC) $< 
dep : gccmakedep $(SRCS)

# DO NOT DELETE
main.o: main.c /usr/include/stdio.h .........
tcp.o: tcp.c 
/usr/include/stdio.h .........
rs232.o: rs232.c /usr/include/stdio.h .........

  하단에# DO NOT DELETE 행과 함께 밑으로 각 .o 에 대한 관련 파일 목록이 자동으로 생성되는 것을 보실 수 있습니다. 이제 make를 실행하면 위 정보에 맞추어 컴파일하게 됩니다.

결언

  어떻게 이해가 되십니까? 마지막 부분은 매크로로 되어 있어서 암호같이 보이는데, 지금 보시면 암호 풀이가 되시는지 모르겠습니다. 소개해 드린 것 보다 더 편리한 부분도 있습니다만 내용이 너무 많아 질 것 같아서 여기서 줄입니다.  긴 내용을 읽어 주셔서 감사합니다.

 

태그: *makefile *C언어 *초보

저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/LINUX

출처  : http://greenfishblog.tistory.com/150


일반적인 빌드 환경(예, Makefile(gcc) / Visual C++, ...) 작성시 다음과 같은 요구 사항이 있습니다.

- DEBUG / RELEASE 컴파일 옵션과 빌드 경로 분리
- 필요한 부분만 re-compile하는 dependency 처리
- Main source 경로 이외의 다른 경로의 source를 함께 빌드
- 각각의 Group 별로 컴파일 설정 관리
- compile 전처리와 후처리의 분리

보통 Visual C++나 QtCreator같은 IDE 툴에서는 프로젝트 셋팅등을 통해 간편하게 위 요구사항을 만족할 수 있지만, Makefile을 이용하는 경우는 생각보단 간단하지 않습니다. 그리고, 잘 정리된 문서 또한 쉽게 구할 수 없습니다. 즉, DIY, 알아서 하라! 입니다.

본 포스트에서는 위 요구사항 뿐 아니라, 일상적인 빌드 환경에서도 요구되는 사항을 만족하는 Makefile 예제를 공유합니다. 해당 Makefile을 통해 Makefile의 구조 학습 뿐만 아니라, 기능을 확장하는데 도움이 되었으면 좋겠습니다. (ps, 보통의 GNU open source는 통상 많은 platform의 지원과 여라가지 이유로 인해 autoconf(./configure)를 사용하지만, 본 포스트는 Makefile에 한해서만 설명합니다. 또한 dist와 install target은 고려하지 않습니다.)

본 포스트의 내용은 길어질 수 있는데, Makefile의 개념 정리 보다, 빨리 개발을 해야 하는 경우에는, 아래의 makefile_sample.zip과 그 안의 Makefile을 확인하면 됩니다. 그리고, 아래쪽의 "응용" 부분으로 바로 넘어가셔도 될 듯 합니다.

예제

우선 이해를 돕기 위해 예제로 부터 시작하겠습니다. 위 sample은 다음과 같은 구성으로 되어 있습니다.

그리고 some_source 경로는 다음과 같습니다.

File1.cpp에 main()가 있으며, 각 File2.c, File3.cpp, File4.cpp에는 foobar2(), foobar3(), foobar4()를 호출합니다. 그리고 defines.h는 FileN.cpp에서 각각 #include하고 있으며, File3.cpp에서는 defines_2nd.h를 #include하는데, 해당 defines_2nd.h는 defines.h를 #include하고 있습니다. 이는 Makefile의 dependency 체크를 위한 테스트를 위한 용도로 설계되었습니다. 위 설명을 그림으로 도식하면 다음과 같습니다.

이러한 .cpp 파일 4개(2개+2개)를 빌드하는 Makefile은 다음과 같습니다.

그리고, 본 포스트에서 예를 들어 설명할 Makefile은 다음과 같습니다.

###############################
# Makefile ver 0.1 by greenfish
###############################
#
# make                          : debug 빌드
# make debug                    : debug 빌드 
# make release                  : release 빌드
# make clean [debug, release]   : 해당 빌드 경로 파일 삭제
# make rebuild [debug, release] : rebuild 실행

# 빌드의 기본값은 debug이며,
# 이곳 section에서는 debug와 release의 공통 부분을 정의한다.
# CFLAGS와 CPPFLAGS는 -c 옵션은 제외한다. rule에서 직접 -c를 넣어야 한다.
# LFLAGS는 Link 옵션으로 필요시 추가한다.
# config는 configuration으로 debug/release를 구별하며, 빌드 경로값에 영향을 준다.
# BUILD_DIR는 빌드에 사용될 경로를 지정한다.
# TARGET은 최종 빌드의 산출물을 지정한다. 빌드 상대 경로를 포함시킨다.
CC        = g++
CFLAGS    = -I/usr/local/include
CPPFLAGS  = -I/usr/local/include
LFLAGS    =
CONFIG    = debug
BUILD_DIR = ./Build/$(CONFIG)
TARGET    = $(BUILD_DIR)/Execute

##################################
# makefile에 들어온 argument를 구한다.
ifneq "$(findstring clean, $(MAKECMDGOALS))" ""
    ARG.CLEAN  = 1
endif

ifneq "$(findstring release, $(MAKECMDGOALS))" ""
    ARG.RELEASE = 1
endif

ifneq "$(findstring debug, $(MAKECMDGOALS))" ""
    ARG.DEBUG = 1
endif

ifneq "$(findstring rebuild, $(MAKECMDGOALS))" ""
    ARG.REBUILD = 1
endif

##################################
# DEBUG / RELEASE 빌드를 설정한다.
ifeq ($(ARG.RELEASE),1)
    # ------------------------------
    # release에 특화된 설정을 한다.
    # ------------------------------
    CFLAGS    += -O -DNDEBUG
    CPPFLAGS  += -O -DNDEBUG
    CONFIG    = release
else
    # ------------------------------
    # debug에 특화된 설정을 한다.
    # ------------------------------
    CFLAGS    += -DDEBUG -g
    CPPFLAGS  += -DDEBUG -g
    CONFIG    = debug
endif

##################################
# makefile 실행 처리

.PHONY: debug release build clean rebuild PRE_BUILD POST_BUILD all

# 일반적인 Build의 스텝을 결정한다.
BUILD_STEP = PRE_BUILD $(TARGET) POST_BUILD

# Makefile의 최종 Target과 Depend를 결정한다.
ifeq ($(ARG.REBUILD),1)
    # rebuild인 경우,...
    # 빌드 이전에 clean을 수행한다.
    rebuild: | clean $(BUILD_STEP)
    debug: ; @true
    release: ; @true
else ifeq ($(ARG.CLEAN),1)
    # clean인 경우,...
    # clean target은 rule part에 정의되어 있다.
    release: ; @true
    debug: ; @true
else
    # clean/rebuild와 함께 쓰이지 않은 경우,...
    # 이곳에서 빌드가 이뤄진다.
    # release, debug는 단독 사용할 수 있어 @true하지 않는다.
    build: | $(BUILD_STEP)
    release: build
    debug: build
endif

# release, debug가 명령에 포함되어 조합되면,
# release, debug target을 찾게 되는데,
# 의도하지 않은
#    "make: Nothing to be done for 'release'"
#    "make: Nothing to be done for 'debug'"
# 를 방지하기 위해 @true를 사용하였다.

# ------------------------------
# Other macro
# ------------------------------
# .o를 .d로 바꿔준다.
DEPEND_FILE = $(patsubst %.o,%.d,$@)

##################################
# 빌드 항목 구성
##################################

# Group은 빌드 환경 설정(컴파일 옵션, 빌드 경로, 소스 확장자, ...)이 같은 것을 묶은 것을 말한다.

#--------------------------------
# 주의 : ../../ 와 같이 상위 directory로 넘어가는 source는 가져오면 안된다.
# 왜냐하면, ./Build/ 밑으로 모든 빌드 파일이 들어가야 하는데,
# ../../등이 포함되면 ./Build/ 상위로 빌드 파일이 들어갈 수 있기 때문에, 위험하다.
# 즉, 다음의 기본 가정을 지켜야 한다.
# `makefile의 위치는 모든 빌드되는 source의 최상위 경로에 있어야 한다.'

##################################
# Group 01
GROUP.01.SRC = File1.cpp               \
               ./some_source/File3.cpp \
               ./some_source/File4.cpp
GROUP.01.OBJ = $(addprefix $(BUILD_DIR)/,$(GROUP.01.SRC:.cpp=.o))
GROUP.01.DEP = $(GROUP.01.OBJ:.o=.d)

##################################
# Group 02
GROUP.02.SRC = File2.c
GROUP.02.OBJ = $(addprefix $(BUILD_DIR)/,$(GROUP.02.SRC:.c=.o))
GROUP.02.DEP = $(GROUP.02.OBJ:.o=.d)

##################################
# [Link] Link Part
# 빌드된 .o 들을 링크하여 TARGET을 생성한다. 
$(TARGET): $(GROUP.01.OBJ) $(GROUP.02.OBJ)
	@echo ----------------------------------------
	@echo Link : $(TARGET)
	@echo ----------------------------------------
	$(CC) $(LFLAGS) $(GROUP.01.OBJ) $(GROUP.02.OBJ) -o $(TARGET)
	@echo

##################################
# [Compile] Compile Part
# Group1 소스(*.cpp)들에 대해, 대상경로($(@D))를 생성하고, dependency 파일을 생성한뒤 컴파일 한다.
$(GROUP.01.OBJ): $(BUILD_DIR)/%.o: %.cpp
	@echo ----------------------------------------
	@echo Compile : [GROUP.01] $<
	@echo ----------------------------------------
	@test -d $(@D) || mkdir -p $(@D)
	$(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $(CFLAGS) $<
	$(CC) $(CFLAGS) -c -o $@ $<
	@echo

$(GROUP.02.OBJ): $(BUILD_DIR)/%.o: %.c
	@echo ----------------------------------------
	@echo Compile : [GROUP.02] $<
	@echo ----------------------------------------
	@test -d $(@D) || mkdir -p $(@D)
	$(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $(CFLAGS) $<
	$(CC) $(CFLAGS) -DSPECIAL_DEFINE -c -o $@ $<
	@echo
	
##################################
# Build가 시작되었다.
# 필요한 전처리가 있다면 구현한다.
PRE_BUILD:
	@echo ========================================
	@echo Make file started [config =\> $(CONFIG)]
	@echo ========================================
	@echo 

##################################
# Build가 종료되었다.
# 필요한 후처리가 있다면 구현한다.
POST_BUILD:
	@echo ========================================
	@echo Make file finished [config =\> $(CONFIG)]
	@echo ========================================

##################################
# Clean up 한다.
clean:
	rm -f $(GROUP.01.OBJ)
	rm -f $(GROUP.01.DEP)
	rm -f $(GROUP.02.OBJ)
	rm -f $(GROUP.02.DEP)
	rm -f $(TARGET)
	@echo ----------------------------------------
	@echo Clean work finished [config =\> $(CONFIG)]
	@echo ----------------------------------------

##################################
# 끝 부분에 dependency 파일들을 include한다.
-include $(GROUP.01.DEP)
-include $(GROUP.02.DEP)

##########################################################
# document
##########################################################
#
# Group 추가 요령
#   - 추가한다는 것은 다른 Group과 비교하여 서로간에,
#       ; 빌드 환경이 다른 경우 (컴파일 옵션, 빌드 경로, 소스 확장자, ...) 
# 가 발생하였기 때문이다.
#
# 1. Group 정의 추가
#
# Group 선언부에 아래 부분을 추가한다.
# 만일 여러 파일이 동일한 sub directory에 있는 경우 addprefix를 이용할 수 있다.
#
# ##################################
# # Group 03
# GROUP.03.SRC = G1.cpp G2.cpp
# GROUP.03.SRC.TMP = G3.cpp G4.cpp G5.cpp G6.cpp G7.cpp
# GROUP.03.SRC += $(addprefix ./some_group/, $(GROUP.03.SRC.TMP))
# GROUP.03.OBJ = $(addprefix $(BUILD_DIR)/,$(GROUP.03.SRC:.cpp=.o))
# GROUP.03.DEP = $(GROUP.03.OBJ:.o=.d)
#
# 2. Link 추가
#
# Target에 아래와 같이 GROUP.03.OBJ를 depend하여 Link되도록 한다.
#
# $(TARGET): $(GROUP.01.OBJ) $(GROUP.02.OBJ) $(GROUP.03.OBJ)
#
# 3. Compile 추가
#
# 아래와 같은 규칙을 추가한다. 확장자나 컴파일 옵션등을 따로 설정할 수 있다.
#
# $(GROUP.03.DEP): $(BUILD_DIR)/%.d: %.cpp
#	@echo ----------------------------------------
#	@echo Depend : $<
#	@echo ----------------------------------------
#	@test -d $(@D) || mkdir -p $(@D)
#	$(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $<
#	$(CC) $(CFLAGS) -c -o $@ $<
#	@echo 
#
# 4. clean 추가
#
#  	rm -f $(GROUP.03.OBJ)
# 	rm -f $(GROUP.03.DEP)
#
# 5. include 추가
#
# -include $(GROUP.02.DEP)
#
# * 지원
# make
# make debug
# make release
# make clean [release, debug]
# make rebuild [release, debug]
#
# (EOF)

위 Makefile을 이용하여 빌드하면 다음과 같습니다.

make
========================================
Make file started [config => debug]
========================================

----------------------------------------
Compile : [GROUP.01] File1.cpp
----------------------------------------
g++ -MM -MF Build/debug/File1.d -MT"Build/debug/File1.o" File1.cpp
g++ -I/usr/local/include -DDEBUG -g -c -o Build/debug/File1.o File1.cpp

----------------------------------------
Compile : [GROUP.01] ./some_source/File3.cpp
----------------------------------------
g++ -MM -MF Build/debug/./some_source/File3.d -MT"Build/debug/./some_source/File3.o" ./some_source/File3.cpp
g++ -I/usr/local/include -DDEBUG -g -c -o Build/debug/./some_source/File3.o ./some_source/File3.cpp

----------------------------------------
Compile : [GROUP.01] ./some_source/File4.cpp
----------------------------------------
g++ -MM -MF Build/debug/./some_source/File4.d -MT"Build/debug/./some_source/File4.o" ./some_source/File4.cpp
g++ -I/usr/local/include -DDEBUG -g -c -o Build/debug/./some_source/File4.o ./some_source/File4.cpp

----------------------------------------
Compile : [GROUP.02] File2.c
----------------------------------------
g++ -MM -MF Build/debug/File2.d -MT"Build/debug/File2.o" File2.c
g++ -I/usr/local/include -DDEBUG -g -DSPECIAL_DEFINE -c -o Build/debug/File2.o File2.c

----------------------------------------
Link : ./Build/debug/Execute
----------------------------------------
g++  ./Build/debug/File1.o ./Build/debug/./some_source/File3.o ./Build/debug/./some_source/File4.o ./Build/debug/File2.o -o ./Build/debug/Execute

========================================
Make file finished [config => debug]
========================================
./Build/debug/Execute
foobar, define value=1
foobar 2, define value=1
foobar 3, 2nd value=2
foobar 4, define value=1
 

즉, 위와 같이 빌드가 진행되었으며, foobar, foobar 2, ..., foobar 4까지 제대로 출력됨을 확인하였습니다.
전체적으로 대략 Compile -> Link 순서로 빌드가 진행됨이 확인됩니다.
예제 Makefile은 다음 옵션을 지원합니다.

  • make [debug, release]
    ; 각 빌드(debug, release) 설정으로 빌드한다. 기본값으로 debug 이다.
  • make clean [debug, release]
    ; 각 빌드(debug, release) 경로를 clean한다. 기본값으로 debug 이다.
  • make rebuild [debug, release]
    ; 각 빌드(debug, release) 설정으로 clean 후 다시 빌드한다. 기본값으로 debug이다.

그리고 빌드하면 다음과 같은 구조를 가지게 됩니다.

/tmp/makefile_sample/Build/debug$ ls -laR
.:
total 40
drwxr-xr-x 3 greenfish greenfish 4096 2013-01-28 09:48 .
drwxr-xr-x 3 greenfish greenfish 4096 2013-01-28 09:48 ..
-rwxr-xr-x 1 greenfish greenfish 9819 2013-01-28 09:48 Execute
-rw-r--r-- 1 greenfish greenfish   41 2013-01-28 09:48 File1.d
-rw-r--r-- 1 greenfish greenfish 2844 2013-01-28 09:48 File1.o
-rw-r--r-- 1 greenfish greenfish   39 2013-01-28 09:48 File2.d
-rw-r--r-- 1 greenfish greenfish 2640 2013-01-28 09:48 File2.o
drwxr-xr-x 2 greenfish greenfish 4096 2013-01-28 09:48 some_source

./some_source:
total 24
drwxr-xr-x 2 greenfish greenfish 4096 2013-01-28 09:48 .
drwxr-xr-x 3 greenfish greenfish 4096 2013-01-28 09:48 ..
-rw-r--r-- 1 greenfish greenfish  114 2013-01-28 09:48 File3.d
-rw-r--r-- 1 greenfish greenfish 2672 2013-01-28 09:48 File3.o
-rw-r--r-- 1 greenfish greenfish   85 2013-01-28 09:48 File4.d
-rw-r--r-- 1 greenfish greenfish 2676 2013-01-28 09:48 File4.o

즉, debug에 대한 빌드 경로는 /Build/debug이며, 그 형식은 소스 경로와 동일합니다. 즉, 소스에 있던 some_source directory가 빌드 경로에도 그대로 반영됩니다(이는 각 경로에 동일한 파일명에 대한 처리를 위해서 입니다). 그리고 빌드 경로의 root에 최종 산출물인 실행 가능한 파일(Execute)가 있습니다. 이러한 구조는 release 빌드에서도 동일하게 반경됩니다.

Makefile Layout

Makefile은 http://www.gnu.org/software/make/manual/make.html(한글)백서를 통해 학습할 수 있습니다. 사전지식은 해당 링크에서 확인하시기 바랍니다. DIY!!!
우선, 위 공유한 Makefile의 전체적인 layout은 다음과 같습니다.

configuration part에서는 빌드를 위한 변수 선언 및 환경 설정을 하게 됩니다. 그다음으로 Makefile에서 가장 중요한 rule part로 진행됩니다.

규칙

makefile의 규칙(rule)은 대략 다음과 같은 형식으로 나열됩니다.

OUTPUT_NAMEINPUT_NAME01 INPUT_NAME02 INPUT_NAME03 ...
    ... script code (tab키로 시작)

Makefile의 백서에서는 targets : prerequisites로 표현됩니다. 위에서는 다소 다르게 표현한 이유는, input/output의 개념으로 설명하려고 했기 때문입니다. 즉, INPUT_NAMExx 등등의 항목으로 OUT_NAME이 형성됩니다. 즉, OUTPUT_NAME이 만들어지기 위해서는 INPUT_NAMExx가 필요하다는 뜻입니다. 그리고 이는, 현재 OUTPUT_NAME이 만들어져있고, INPUT_NAMExx가 변경이 없었다면, OUTPUT_NAME의 처리는 skip된다는 뜻도 있습니다. 그리고, 위와 같이 Makefile에 있는 OUTPUT_NAME 규칙은 실제로 언제 실행되냐면, 빌드 Target이 OUTPUT_NAME을 필요로 할때 실행이 됩니다. 즉,

MAINOUTPUT_NAME
    ... script code (tab키로 시작)

OUTPUT_NAMEINPUT_NAME01 INPUT_NAME02 INPUT_NAME03 ...
    ... script code (tab키로 시작)

main.omain.cpp main.h function.cpp

위와 같은 상황에서 make MAIN을 실행하게 되면, OUTPUT_NAME이 실행하게 됩니다. 만일 make XXXX를 실행한다면,MAIN과 OUTPUT_NAME은 실행되지 않으며, XXXX가 없기 때문에 다음 오류가 발생합니다.

$ make XXXX
make: *** No rule to make target `XXXX'.  Stop.

OUTPUT_NAME이 실행되려면 INPUT_NAME01, INPUT_NAME02...이 준비가 되어야 합니다. 즉, INPUT_NAMExx에 해당되는 rule을 찾아 먼저 실행하게 됩니다.
여기에서, OUTPUT에 해당되는 OUTPUT_NAME은 일반적인 Target 이름이 되는데, Target 이름이 아니더라도, 파일 이름이 되기도 합니다. 즉, main.o는 main.cppmain.hfunction.cpp를 가지고 실행하게 됩니다. 역시 다시 말하자면 해당 .cpp/.h의 변경 사항이 있을때 main.o에 해당되는 rule이 실행됩니다.
그리고 예제 Makefile에는 다음과 같은 중요한 암묵적 규칙(implicit rule, 백서)이 포함됩니다.
이러한 암묵적 규칙은 다음과 같이 설명됩니다.

a.o b.o c.o%.o: %.c
    ... $@ ... $<

알기 힘든 암호같은 문장으로 되어 있는데, 우선 a.o b.o c.o는 %.o: %c라는 패턴으로 동작하게 됩니다. %는 wildcard와 같이 이해하면 되는데, .o->.c 즉, a.o b.o c.o는 a.c b.c c.c 파일의 존재여부를 찾아 실행하게 됩니다. 그리고 $@는 *.o에 해당되고$<는 *.c에 해당됩니다. 이곳이 뭔가 이해나 가독성이 떻어진다면, 다음과 같이 이해하면 됩니다.
우선 제일 왼쪽의 a.o b.o c.o는 이것들을 "생산"한다라고 이해하면 됩니다. 즉, OUTPUT에 해당되니깐요. 그리고, %.o:%.c인데, 이것 또한 OUPUT: INPUT이라 보면 됩니다. 즉, .c를 가지고 .o를 "생산"한다고 보시면 됩니다. 그리고 아래에 있는$@와 $<인데, @는 o와, 그리고 <는 c와 모양이 비슷합니다. 즉, 각각의 .o에 해당 되는 파일은 $@에 해당되고, .c 파일은 $<에 연관된다고 보면 이해가 빠를 것입니다.
다시 설명하자면 위 암묵적 규칙은 다음과 같이 확장된다고 생각하면 됩니다. (이렇게 설명하는 것이 옳은지 모르겠습니다. 단순히 이해를 돕는 차원입니다.)

a.oa.c
    ... a.o ... a.c
b.ob.c
    ... b.o ... b.c
c.oc.c
    ... c.o ... c.c 

이러한 암묵적 규칙을 이용하면 효과적인 빌드 환경을 구축하는데 도움을 줍니다.
물론, 위는 다음과 같이 일반적인 규칙으로 사용할 수 있습니다. 이렇게 되면 한번의 gcc 호출로 여러개의 .o를 만들게 됩니다.

a.o b.o c.oa.c b.c c.c
    ... a.o b.o c.o ... a.c b.c c.c 

장/단점을 따질 수는 없지만, 저같은 경우에는 여러번 gcc를 실행하되 한번에 한개의 파일을 compile하는 편을 선호해서 암묵적 규칙으로 Makefile을 구성하였습니다. 어떻든 선택은 자유입니다. (저는 효율보다는 효과를 중요시 합니다.)

dependency

위 예제에서, define_2nd.h는 File3.cpp에서만 사용되는 header file입니다. 해당 파일이 변경되면 당연히 다시 빌드를 해야 합니다. gcc를 사용하는 Makefile은 다음과 같이 .d라는 dependency 파일을 만들면 자동으로 해결할 수 있습니다.

make clean
(...생략...)
----------------------------------------
Clean work finished [config => debug]
----------------------------------------
make
========================================
Make file started [config => debug]
========================================

----------------------------------------
Compile : [GROUP.01] File1.cpp
----------------------------------------
g++ -MM -MF Build/debug/File1.d -MT"Build/debug/File1.o" File1.cpp
g++ -I/usr/local/include -DDEBUG -g -c -o Build/debug/File1.o File1.cpp

----------------------------------------
Compile : [GROUP.01] ./some_source/File3.cpp
----------------------------------------
(...생략...)
========================================
Make file finished [config => debug]
========================================

즉 위와 같이 빌드를 진행합니다.
그리고 아무런 코드 수정없이 다시 make를 하면 아래와 같이 빌드과정 없이 바로 종료하게 됩니다.

make
========================================
Make file started [config => debug]
========================================

========================================
Make file finished [config => debug]
========================================

그런다음, File3.cpp에서 사용하고 있는 define_2nd.h를 수정하겠습니다. 이는 linux의 touch를 이용하는 것으로, 실제로 내용 수정은 없지만 File의 timestamp를 변경하여 수정하는 것으로 하였습니다.

$ touch defines_2nd.h

그런다음 아래와 같이 다시 make를 하면, File3.cpp만 컴파일한뒤 링크를 실행하게 됩니다.

make
========================================
Make file started [config => debug]
========================================

----------------------------------------
Compile : [GROUP.01] some_source/File3.cpp
----------------------------------------
g++ -MM -MF Build/debug/./some_source/File3.d -MT"Build/debug/./some_source/File
3.o" some_source/File3.cpp
g++ -I/usr/local/include -DDEBUG -g -c -o Build/debug/./some_source/File3.o some
_source/File3.cpp

----------------------------------------
Link : ./Build/debug/Execute
----------------------------------------
g++  ./Build/debug/File1.o ./Build/debug/./some_source/File3.o ./Build/debug/./s
ome_source/File4.o ./Build/debug/File2.o -o ./Build/debug/Execute

========================================
Make file finished [config => debug]
========================================

이는 gcc(g++)의 -MM -MF -MT 명령을 이용해 dependency 파일을 만들어 Makefile에서 연결했기 때문에 가능한 일입니다. 해당 파일(.d)은 다음과 같습니다.

cat ./Build/debug/some_source/File3.d
Build/debug/./some_source/File3.o: some_source/File3.cpp \
 some_source/../defines_2nd.h some_source/../defines.h

즉, File3.o는 File3.cpp, defines_2nd.h, 그리고 define.h로 구성된다고 알려주는데, Makefile의 규칙 표현 방식으로 구성됩니다. 그래서 Makefile의 마지막에 해당 .d들을 include하여 Makefile의 규칙을 확장하게 됩니다. 참고로, include 앞에있는 -는 해당 .d가 없더라도 오류없이 넘어가도록 해주는 장치입니다.

##################################
# 끝 부분에 dependency 파일들을 include한다.

-include $(GROUP.01.DEP)
-include $(GROUP.02.DEP)

Makefile로 빌드시 즉, compile할 때, File3.o를 Target으로 만드는 과정이 있는데, 해당 .d를 include하였기 때문에, 위 규칙(즉, File3.o 생성에 대한)이 실행되기전, dependency 체크가 이뤄지게끔 하는 것입니다. 만일 include하지 않았다면 빌드 오류는 없겠지만, define_2nd.h가 아무리 변경되더라도 File3.o나 Execute파일을 다시 만들어 주지 못합니다. 물론 암묵적 규칙의 %.c를 통해 File3.cpp가 변경되는 경우에는 include없이도 File3.o와 Execute파일을 다시 만들수는 있습니다.
해당 dependency 파일은 compile전, 즉, gcc -c ...를 실행하기 전에 미리 실행하게 됩니다. (그 순서는 사실 크게 중요하지 않습니다). 따라서, 규칙의 실행은 아래와 같이 dependency 파일 생성과 compile이 batch로 동작하는 방식입니다.

$(GROUP.01.OBJ)$(BUILD_DIR)/%.o%.cpp
    $(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $<
    $(CC) $(CFLAGS) -c -o $@ $<


group

group은 Makefile 백서에 등장하지 않는 개념입니다. 즉, 제가 만든 형식입니다. group은 동일한 규칙을 받아 들이는 소스 파일 리스트로 정의하였습니다. 즉, 컴파일 옵션이나 대상 경로, 그리고 파일 확장자가 같다면 동일한 group으로 인정합니다. 즉, group은 다음과 같이 선언됩니다.

##################################
# Group 01
GROUP.01.SRC = File1.cpp               \
               ./some_source/File3.cpp \
               ./some_source/File4.cpp
GROUP.01.OBJ = $(addprefix $(BUILD_DIR)/,$(GROUP.01.SRC:.cpp=.o))
GROUP.01.DEP = $(GROUP.01.OBJ:.o=.d)

이와 같이 group은 소스 파일을 입력하고, 그 파일을 기반으로 object 파일과 dependency 파일의 경로 선언으로 이뤄져 있습니다(주의점은, .h와 같이 object를 생성하지 못하는 파일을 넣어서는 안됩니다. 그래서 .h의 변경사항시 재빌드를 만들기 위해 앞선 chapter와 같이 dependency를 명시적으로 만들어 넣게 되었습니다). 즉, 사용자는 단순히 "소프(.c, .cpp) 파일 리스트 추가" 작업만 진행하면 됩니다!
암묵적 규칙의 특징으로, 하나의 규칙은 하나의 동일한 확장자만 입력받도록 되어 있습니다(백서를 보면, OBJ%.o:: 혹은 OBJ:%o%로 설명되어 있는데, 빌드 경로가 분리되어서 그런지, 아니면 dependency 파일이 include되어서 그런지, 실제로 동작되지 않더군요. 혹시 가능하게 만들 수 있을까요?). 위 예에서는 File2.c가 예외 case로, 확장자가 .c이기 때문에 GROUP.01.OBJ에 해당되는 규칙으로는 실행되지 않습니다. 그래서 빌드 오류가 발생하게 됩니다. 따라서, GROUP.02.OBJ를 만들어 따로 관리하여 만들게 되었습니다. 또한 위 예에서, GROUP.02.OBJ를 컴파일할때는 -DSPECIAL_DEFINE를 추가하여 빌드하였습니다. 이런 것들이 GROUP.01과 GROUP.02를 구별하게 하는 요소입니다.

make 실행과 debug, release 분리

make가 실행되면 해당 경로의 makefile 혹은 Makefile을 찾게 됩니다. make 뒤에는 Makefile이 실행할 main target을 옵션으로 명시할 수 있습니다. 또는 Makefile에 전달할 변수를 전달할 수 있습니다. 따라서, make를 실행은 다음과 같은 모습일 겁니다.

make debug
...
make DEBUG=1
...

즉, 첫번째 방법은 makefile내의 debug target을 실행해라는 것이고, 두번째 방법은 DEBUG 변수값에 1을 전달하여 실행해라는 것으로, 저는 첫번째 방법으로 구성하였습니다(보다 사람에게 친숙한 표현이니깐요).
우선, makefile의 선두의 configuration 부분을 보겠습니다.

CC        = g++
CFLAGS    = -I/usr/local/include
CPPFLAGS  = -I/usr/local/include
LFLAGS    =
CONFIG    = debug
BUILD_DIR = ./Build/$(CONFIG)
TARGET    = $(BUILD_DIR)/Execute

CFLAGS나 CPPFLAGS등은 google을 통해 확인하십시요. 여기에서 중요한건 CONFIG와 BUILD_DIR입니다. 즉, CONFIG는 debug이냐 release이냐를 판단하는 부분입니다. 현재는 debug, release만 지원하는데, 그 이외에도 static_release와 같이 여러개를 만들 수 있습니다. 물론, 그에 따라 makefile도 수정해야 겠죠. 일단 기본값은 debug이며 빌드의 경로는 해당 config 값으로 결정되도록 하였습니다.
그다음 부분은 아래와 같습니다.

##################################
# makefile에 들어온 argument를 구한다.

ifneq "$(findstring clean, $(MAKECMDGOALS))" ""
    ARG.CLEAN  = 1
endif

ifneq "$(findstring release, $(MAKECMDGOALS))" ""

    ARG.RELEASE = 1
endif
...

즉, makefile로 들어온 argument 값을 저장하고 있습니다. 만일 make clean이 실행되었다면 ARG.CLEAN은 1이 됩니다.

##################################
# DEBUG / RELEASE 빌드를 설정한다.

ifeq ($(ARG.RELEASE),1)
    # ------------------------------
    # release에 특화된 설정을 한다.
    # ------------------------------

    CFLAGS    += -O -DNDEBUG
    CPPFLAGS  += -O -DNDEBUG
    CONFIG    = release
else
    # ------------------------------
    # debug에 특화된 설정을 한다.
    # ------------------------------

    CFLAGS    += -DDEBUG -g
    CPPFLAGS  += -DDEBUG -g
    CONFIG    = debug
endif

위 부분에서 debug와 release의 구분을 만들고 있습니다. 컴파일 옵션을 변경하는데, debug에서는 debug관련 설정을 추가하고, release에서는 release관련 설정을 넣었습니다. 역시 중요한건 CONFIG로 release인 경우에는 release 값을 전달하여 빌드 경로가 ./Build/release가 될 것입니다.

##################################
# makefile 실행 처리

.PHONY: debug release build clean rebuild PRE_BUILD POST_BUILD all

# 일반적인 Build의 스텝을 결정한다.

BUILD_STEP = PRE_BUILD $(TARGET) POST_BUILD

#
 Makefile의 최종 Target과 Depend를 결정한다.
ifeq ($(ARG.REBUILD),1)
    # rebuild인 경우,...
    # 빌드 이전에 clean을 수행한다.

    rebuild: | clean $(BUILD_STEP)
    debug: ; @true
    release: ; @true
else ifeq ($(ARG.CLEAN),1)
    # clean인 경우,...
    # clean target은 rule part에 정의되어 있다.

    release: ; @true
    debug: ; @true
else
    # clean/rebuild와 함께 쓰이지 않은 경우,...
    # 이곳에서 빌드가 이뤄진다.
    # release, debug는 단독 사용할 수 있어 @true하지 않는다.

    build: | $(BUILD_STEP)
    release: build
    debug: build
endif

우선 phony를 지정하고 있는데, 해당 target은 file과는 상관없는 의도적으로 지정된 이름으로 사용하겠다는 것입니다. 그리고, BUILD_STEP을 만들었는데, 선처리, 후처리가 포함되었습니다. makefile의 최종 산출물은 $(TARGET) 규칙이기 때문에, 해당 rule이 중간에 포함되었습니다.
그리고 make rebuild 실행을 처리하는 부분이 있는데, rebuild라는 규칙을 새로 정의하였습니다. 많일 해당 규칙을 정의하지 않으면 make rebuild를 실행하면, rebuild 규칙이 없다고 오류가 발생할 것입니다. 그리고 규칙의 input에 pipe(|) 문자를 넣었는데, 이는 규칙의 순서를 명시적으로 지켜라라는 의미입니다. 즉, clean -> $(BUILD_STEP)으로 진행해라는 뜻입니다. 만일 pipe 문자가 없다면 makefile이 병렬로 처리될 때 문제(즉, 순서가 바뀌는)가 발생할 수 있습니다.
그리고 특이한건, semi-colon(;)의 사용인데, 세미콜론 뒤의 내용은 규칙의 recipe(실행 스크립트)를 의미합니다. 즉, 규칙을 한줄로 표현한 것입니다. 또, 연속해서 @true가 사용되었는데, 해당 규칙이 의미있게 실행되었다고 밝히는 것으로 이해하면 됩니다. 만일, 아래와 같이 정의되었다면,

ifeq ($(ARG.REBUILD),1)
    # rebuild인 경우,...
    # 빌드 이전에 clean을 수행한다.

    rebuild: | clean $(BUILD_STEP)
    debug: rebuild
    release: rebuild

다음과 같이 "make: Nothing to be done for 'release'"와 같은 경고가 발생할 수 있습니다.

make rebuild release
...
make: Nothing to be done for 'release'

현재 make뒤에 따라온 release와 debug는 실제로 의미있는 target은 아닙니다. 그냥 상태값을 전달해 주기 위한 용도로 사용되었습니다. 만일 make뒤에 release가 실행되었는데, release 규칙은 있어서 실행하였지만 아무런 스크립트가 실행되지 않았을 경우에 위 경고가 발생하는 것입니다. 즉, 위에서 release: rebuild로 선언하였는데, $ makefile rebuild release가 실행되면 makefile에 전달된 rebuild는 의미있게 스크립트로 연결되어 실행이 되었는데, release는 이미 실행된 rebuild로 또 연결되어, 아무런 영향을 주지 못하기 때문입니다. 그래서, release 규칙은 @true라는 지시자로 연결하였는데, 이 경우는 make에 "잘 수행 하였다"를 전달해 주기 때문에 위 경고가 표시되지 않습니다. 향후 makefile의 명령을 추가하려면 이와 같은 사항을 유의하시기 바랍니다.

응용

예제로 등록된 makefile을 응용하는 방법을 공유하겠습니다.

일반적으로 보통 빌드환경에서 거의 모든 컴파일 옵션은 동일할 것입니다. 그래서 왠만해서는 GROUP의 개수는 1개로 유지될 것입니다. 만일 빌드에 파일(.c / .cpp)이 추가되었을 경우, 

GROUP.01.SRC = File1.cpp               \
               ./some_source/File3.cpp \
               ./some_source/File4.cpp \
               ./File999.cpp

와 같이 GROUP.01.SRC의 값만 변경하면 됩니다.  보통 이 정도의 수준으로 진행될 것입니다.
물론, CFLAGS나 CPPFLAGS 설정은 사용자가 직접 검토해 보시기 바랍니다.

만일, SQLITE와 같이 확장자가 .c로 구성되거나, 특정 소스들에게만 -DSQLITE_CORE와 같은 것이 포함되어야 한다면, GROUP 추가를 고려해야 합니다. 추가하는 방법은 예제 Makefile 끝 부분 주석을 참고하시면 됩니다.

흔하지 않겠지만, 만일 debug, release 말고 static_release같은 것이 추가되어야 한다면 대략 다음과 같은 부분이 추가로 들어갈 것입니다.

ifneq "$(findstring static_release, $(MAKECMDGOALS))" ""
    ARG.STATIC_RELEASE = 1
endif

...

else ifeq ($(ARG.STATIC_RELEASE),1)
    CFLAGS    += something...
    CPPFLAGS  += something...
    LFALGS    += something...
       
    CONFIG    = static_release
endif

...

.PHONY: ... static_release ...

...

ifeq ($(ARG.STATIC_RELEASE),1)
    static_release: SOMETHING rule targets
    ...

...

GROUP.STATIC_RELEASE.SRC = GROUP.01.SRC
GROUP.STATIC_RELEASE.SRC += ...

...

와 같은 과정이 필요할거 같습니다. 명령이 들어왔는지 검토하는 부분은 반듯이 있어야 할 것이며, 그에 따라 CONFIG를 설정하여 빌드 경로를 분리합니다. 또한 static이라면 LFLAGS도 수정될 수 있습니다. 소스 파일 리스트의 차이가 있거나 컴파일 환경등이 다른 경우에 대비하여 새로운 GROUP을 만드는 것도 좋은 방법일 듯 합니다.

$(GROUP.STATIC_RELEASE.OBJ): $(BUILD_DIR)/%.o: %.c
    @echo ----------------------------------------
    @echo Compile : [GROUP.STATIC_RELEASE] $<
    @echo ----------------------------------------
    @test -d $(@D) || mkdir -p $(@D)
    $(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $<
    $(CC) $(CFLAGS) -DSTATIC_LIB -c -o $@ $<
    @echo

위와 같이 이전의 group과 다른 옵션이 들어갈 수 있습니다.
만일 $(TARGET)의 input이 달라져야 한다면, 다음과 같이 수정할 수 있습니다.

...
TARGET     = $(BUILD_DIR)/Execute
TARGET_DEP = $(GROUP.01.OBJ) $(GROUP.02.OBJ)
...
else ifeq ($(ARG.STATIC_RELEASE),1)
    TARGET     = $(BUILD_DIR)/static_lib
    TARGET_DEP = $(GROUP.STATIC_RELEASE.OBJ)
...
$(TARGET): $(TARGET_DEP)
...

즉, 위와 같이 하면, static_release인 경우에만, $(TARGET)의 dependency를 달리 하여, 다른 빌드에는 영향은 없을 것입니다.

Revision

First :

R2 : 2013/02/04

depend 파일 (.d)를 만드는 과정에서 compile 에러가 발생할 소지가 있습니다. 왜냐하면, depend 파일을 만들때 compile 옵션이 모두 포함되지 않았기 때문입니다. 만일 -I와 같은 옵션이 CFLAGS에 정의되어 있다면, .d파일을 만들때 오류가 발생하게 됩니다. 왜냐하면, 해당 옵션이 포함되지 않았기 때문입니다.
다음과 같이 $(CFLAGS)가 추가되었습니다.

$(GROUP.01.OBJ): $(BUILD_DIR)/%.o: %.c
	...
	$(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $(CFLAGS) $<
	...
$(GROUP.02.OBJ): $(BUILD_DIR)/%.o: %.c
	...
	$(CC) -MM -MF $(DEPEND_FILE) -MT"$(DEPEND_FILE:.d=.o)" $(CFLAGS) $<
	...


저작자 표시 비영리 변경 금지
신고
3 4
Software Architect/LINUX

   퍼미션(권한)이란?

Owner

Group

Other

Owner와 Group은 파일소유자자신과 자신이 속한그룹. Other은 제3자, 웹사이트 방문객은 제3자로 nobody로 취급.

r

w

x

r

w

x

r

w

x

r은 파일 읽기(4), w는 파일 쓰기(2), x는 파일 실행(1)

7

5

5

파일소유자는 그것을 읽고 쓰고 실행시킬 수 있지만, 제3자는 읽고 실행만 시킬 수 있다.

7

7

7

제3자도 쓰기 권한이 주어진다.

*.html  *.cgi, *.pl *.txt등의 파일은 업로드시 반드시 ascii로 하고 나머지 그림(*.gif *.jpg)이나 자바 애플릿(*.class), 실행파일(*.exe *.zip *.rar)등은 binary mode로 업로드 할 것.

   리눅스 기본명령어

명령어

사 용 법

login

사용자 인증과정

리눅스 시스템은 기본적으로 multi-user 개념에서 시작하였기 때문에 시스템을 이용하기 위해서는 반드시 로그인을 하여야 합니 다. 로그인은 PC 통신에서도 많이 사용되어져 왔기 때문에 그 개 념  설정에 그다지 어려움이 없을 것입니다. 흔히 말하는 ID를 입력하는 과정입니다. 

passwd

패스워드 변경

리눅스, 특히 인터넷의 세계에서는 일반 컴퓨팅 상황에 비하여 훨씬 해킹에 대한 위험이 높습니다. 패스워드는 완성된 단어 보다는 단어 중간에 숫자나 키보드의 ^, #, ' 등과 같은 쉽게 연상 할 수 없는 기호를 삽입하여 만들어 주는 것이 좋습니다

du

하드사용량 체크(chkdsk)

자신의 하드공간을 알려면
# du
특정 디렉토리의 사용량을 알려면
# du -s diretory_name

ls

파일 리스트 보기(dir)

F : 파일 유형을 나타내는 기호를 파일명 끝에 표시
    (디렉토리는 '/', 실행파일은 '*', 심볼릭 링크는 '@'가 나타남).
l  : 파일에 관한 상세 정보를 나타냅니다.
a : dot 파일(.access 등)을 포함한 모든 파일 표시.
t  : 파일이 생성된 시간별로 표시
C : 도스의 dir/w명령과 같 이 한줄에 여러개의 정보를 표시
R : 도스의 dir/s 명령과 같이 서브디렉토리 내용까지.

(예)
# ls -al  
# ls -aC
# ls -R

cd

디렉토리를 변경

# cd cgi-bin     : 하부 디렉토리인 cgi-bin으로 들어감.
# cd  ..             : 상위디렉토리로 이동
# cd 또는 cd ~  : 어느곳에서든지 자기 홈디렉토리로 바로 이동
# cd /webker     : 현재 작업중인 디렉토리의 하위나 상위 디렉토리가
                          아닌 다른 디렉토리(webker)로 이동하려면 /로
                          시작해서 경로이름을 입력하면 된다.

cp

화일 복사(copy)

# cp index.html index.old
     : index.html 화일을 index.old 란 이름으로 복사.

# cp /home/test/*.*  .
     : test 디렉토리내의 모든 화일을 현 디렉토리로 복사.

mv

파일이름(rename) / 위치(move)변경

# mv index.htm index.html
     : index.htm 화일을 index.html 로 이름 변경

$ mv file  ../main/new_file
     : 파일의 위치변경

mkdir

디렉토리 생성

# mkdir download  : download 디렉토리 생성

rm

화일삭제

# rm test.html : test.html 화일 삭제
# rm -r <디렉토리> : 디렉토리 전체를 삭제
# rm -i a.*
     : a로 시작하는 모든 파일을 일일이 삭제할 것인지 확인하면서 삭제 

rmdir

디렉토리 삭제

# rmdir cgi-bin : cgi-bin 디렉토리 삭제

pwd

현재의 디렉토리 경로를 보여주기

pico

리눅스용 에디터

put

ftp 상태에서 화일 업로드

> put  guestbook.tar.gz

get

ftp 상태에서 화일 다운로드

> get  guestbook.tar.gz

mput 또는 mget

여러개의 화일을 올리고 내릴때 (put,get과 사용법동일)

chmod

화일 permission 변경

리눅스에서는 각 화일과 디렉토리에 사용권한을 부여.

예) -rwxr-xr-x   guestbookt.html
rwx  :처음 3개 문자 = 사용자 자신의 사용 권한
r-x  :그다음 3개 문자 = 그룹 사용자의 사용 권한
r-x  :마지막 3개 문자 = 전체 사용자의 사용 권한

읽기(read)---------- 화일 읽기 권한
쓰기(write)---------- 화일 쓰기 권한
실행(execution)---------- 화일 실행 권한
없음(-)---------- 사용권한 없음

명령어 사용법
chmod [변경모드] [파일]

# chmod 666  guestbook.html
     : test.html 화일을 자신에게만 r,w,x 권한을 줌

# chmod 766  guestbook.html
     : 자신은 모든 권한을 그룹사용자와,전체사용자에게는
       읽기와 쓰기 권한만 줌

alias

" doskey alias" 와 비슷하게 이용할 수 있는 쉘 명령어 alias는 말그대로 별명입니다. 사용자는 alias를 이용하여 긴 유 닉스 명령어를 간단하게 줄여서 사용할 수도 있습니다.
이들 앨리어스는 [alias ls 'ls -al'] 같이 사용하시면 되는데, 한 번 지정한 alias를 계속해서 이용하시려면, 자신의 홈디렉토리에 있는
.cshrc(Hidden 속성)을 pico등의 에디터를 이용하여 변경시 키면 됩니다.

cat

파일의 내용을 화면에 출력하거나 파일을 만드는 명령( 도스의 TYPE명령)

# cat filename

more

cat 명령어는 실행을 시키면 한 화면을 넘기는 파일일 경우 그 내용을 모두 볼수가 없다. 하지만 more 명령어를 사용하면 한 화면 단위로 보여줄 수 있어 유용.

# more <옵션>
옵션은 다음과 같습니다.

Space bar : 다음 페이지
Return(enter) key : 다음 줄
v : vi 편집기로 전환
/str : str 문자를 찾음
b : 이전 페이지
q : more 상태를 빠져나감
h : 도움말
= : 현재 line number를 보여줌

who

현재 시스템에 login 하고 있는 사용자의 리스트를 보여줍니다.

# who

whereis

소스, 실행파일, 메뉴얼 등의 위치를 알려줍니다

# whereis perl : perl의 위치를 알려준다

vi,
touch,
cat

새로운 파일을 만드는 방법

# vi newfile :  vi 편집기 상태로 들어감
# touch newfile : 빈 파일만 생성됨
# cat > newfile  : vi 편집기 상태로 들어감, 문서 작성후 Ctrl+D로 빠져나옴

cat,
head,
tail

파일 내용만 보기

# cat filename         : 파일의 내용을 모두 보여줌
# head -n filename : n줄 만큼 위세서부터 보여줌
# tail -n filename     : n줄 만큼 아래에서부터 보여줌

 

   압축명령어 사용법

압축 명령어

사 용 법

tar

.tar, _tar로 된 파일을 묶거나 풀때 사용하는 명령어

(압축파일이 아님)


# tar cvf [파일명(.tar, _tar)] 압축할 파일(또는 디렉토리): 묶을때

# tar xvf [파일명(.tar, _tar)]  :  풀 때

   (cf) cvfp/xvfp 로 하면 퍼미션 부동 

compress

확장자 .Z 형태의 압축파일 생성


# compress    [파일명]     : 압축시 

# uncompress [파일명]    : 해제시

gzip

확장자  .gz, .z 형태의 압축파일 생성


#  gzip     [파일명]    : 압축시

#  gzip -d [파일명]   : 해제시

기타

.tar.Z 

이것은 tar로 묶은 후에 compress를 사용하여 압축한 것으로 uncompress를 사용해서 압축을 푼 다음, 

다시 tar를 사용해서 원래의 파일들을 만들어내면 됩니다. 

아니면 다음과 같이 한 번에 풀 수도 있다.

# zcat  [파일명].tar.Z  : 해제시


.tar.gz또는 .tar.z

# gzip -cd [파일명]    : 해제시


.tar.gz 또는 .tar.z .tgz

gzip을 사용해서 푼 다음 다시 tar를 사용해서 원래 파일을 만들어 낼 수 있으나,

하지만 다음과 같이 하면 한 번에 처리를 할 수 있다.


# gzip -cd 파일.tar.gz | tar xvf -  또는

# tar xvzf 파일.tar.gz

# tar xvzf 파일.tgz

 

   리눅스 필수명령어

Linux/Unix 명령어

설 명

MS-DOS 비교

./x

x 프로그램 실행
(현재 디렉토리에 있는 것)

x

/

이전에(↑) / 다음에(↓) 입력했던 명령어

doskey

cd x (또는 cd /x)

디렉토리 X로 가기

cd

cd .. (또는 cd ../ 또는 cd /..)

한 디렉토리 위로 가기

cd..

x 다음 [tab] [tab]

x 로 시작하는 모든 명령어 보기

-

adduser

시스템에 사용자 추가

/

ls (또는 dir)

디렉토리 내부 보여주기

dir

cat

터미널 상의 텍스트 파일 보기

type

mv x y

파일 x를 파일 y로 바꾸거나 옮기기

move

cp x y

파일 x를 파일 y로 복사하기

copy

rm x

파일 지우기

del

mkdir x

디렉토리 만들기

md

rmdir x

디렉토리 지우기

rd

rm -r x

디렉토리 x를 지우고 하위도 다 지우기

deltree

rm p

패키지 지우기

-

df (또는 df x)

장치 x의 남은 공간 보여주기

chkdsk ?

top

메모리 상태 보여주기(q는 종료)

mem

man x

명령어 x에 관한 매뉴얼 페이지 얻기

/

less x

 텍스트 파일 x 보기
(리눅스에서는 더 많은 필터 적용 가능)

type x | more

echo

어떤 것을  echo 화면에 인쇄한다.

echo

mc

UNIX를 위한 노턴 커맨더

nc

mount

장치 연결(예: CD-ROM, 연결을 해제하려면 umount)

-

halt

시스템 종료

-

reboot ([ctrl] + [alt] +[del])

시스템  다시 시작하기

[ctrl] + [del] + [del]

    고급명령어

 고급 명령어

 

chmod <권한> <파일>

파일 권한(permissions) 변경

ls -l x

파일 x의 자세한 상황을 보여줌

ln -s x y

 x에서 y로 심볼릭 링크를 만들어 줌

find x -name y -print

디렉토리 x안에서 파일 y를 찾아서 화면에 그 결과를 보여줌

ps

지금 작동중인 모든 프로세스들을 보여줌

kill x

 프로세스 x를 종료 (x는 ps 명령으로 알 게 된 PID)

[alt] + F1 - F7

 터미널 1-7까지 바꾸기 (텍스트 터미널에서; F7은 X-윈도우(시작될때))

lilo

 부트 디스크를 만듦

 

용어

 

symlink

다른 파일이나 디렉토리로 심볼릭 링크. 윈도유98의 바로가기 같은 것

shell script

여러 명령어들을 차례로 수행하게 한 것. MS-DOS의 배치 파일 같은 것

     팁!!

 - 웹에서 생성한 노바디파일 삭제 하는방법..

기본적으로 웹서버는 nobody 권한으로 동작이 되게 됩니다.
고객님께서 FTP 로 접속하여 전송한 파일이 아니라 웹상에서 사용자들이 파일을 업로드 한 경우나 웹상에서 생성된 파일의 경우 삭제가 되지 않는 경우가 있을 수 있습니다.

웹서버의 동작 권한은 nobody 이고 웹상에서 생성된 파일이므로 해당 파일이 nobody 소유권으로 시스템에 생성이 되게 됩니다.

아래와 같이 웹상에서 실행시키면 됩니다.

1. 메모장을 열어 아래 소스를 붙여넣기 하신후..

<?

//폴더/파일 삭제시

$cmd = `rm -rf 노버디로된파일혹은폴더명`;

echo "$cmd";

echo "폴더가 삭제 되었습니다.";

?>

-- 위에까지..
-- **위에서 수정할 사항은 "노버디로된파일혹은폴더명"을 삭제하시고자 하는 파일명으로 바꿔주세요..

2. 파일 -> 다른이름으로저장 -> 아래 탭에서 파일형식을 "모든파일"로 선택후

   -> "원하는파일명.php" 로 저장 (ex: del.php)

3. ftp를 통해 고객계정에 파일업로드를 하시고 웹에서 파일을 불러주시면 됩니다

   ex: html폴더안에/temp 안에 삭제하고자하는 파일이 있을경우 / html폴더/temp안에 del.php를 업로드하고..

       브라우저에서 http://고객도메인/temp/del.php 를 하면 됩니다

4. 실행하시면 삭제되고 nobody 권한의 폴더만 남습니다.(폴더안의화일들만 지워짐)

   그후 ftp 접속후 폴더를 삭제하시면 됩니다.

ex)

<?

퍼미션 변경시

$cmd = `chmod -R 777 노버디로된파일혹은폴더명`;

echo "$cmd";

echo "퍼미션 변경되었습니다.";

?>

저작자 표시 비영리 변경 금지
신고
1 1
Software Architect/LINUX

출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Vim/Documents/UsedVim



  • line sort
    :{range}sort u
    
  • 개행 문자 변환하기 : Unix는 \n이지만 윈도우는 \r\n이다. 간혹 윈도우에서 만든 문서를 읽다보면 각 줄의 마지막에 ^M이 표시돼서 읽기 불편할 때가 있다. 치환할 수도 있지만 아래와 같이 명령으로 처리할 수도 있다. :set ff=unix
  • 파일 삽입 :r 파일명
  • 명령 실행 결과 삽입 :r! 명령어
  • 외부 text복사시 자동 들여쓰기에 의한 계단 현상 제거.
    set paste
  • 대소문자 변경
    1. 모든 문자를 대문자로 :%s/.*/U 1. 모든 문자를 소문자로 :%s/.*/L 1. 모든 단어의 첫번째 문자를 대문자로 :%s/\<./\u&/g
    2. 모든 단어의 첫번째 문자를 소문자로 :%s/\<./\l&/g
    3. 모든 줄의 첫번째 단어를 대문자로 :%s/.*/\u&
    4. 모든 줄의 첫번째 단어를 소문자로 :%s/.*/\l& 

소식 

단축키 

보낸 사람 Linux

해야될 작업 

  • vim7이 나왔음. 가능한 vim7의 변경된 점을 추가해 볼것
    • ex) split 단축키 지원 추가 : shift+p

참고 

추가해야될 내용 

  1. 같은 문자를 여러번 삽입
    1을 100번 연속 입력해야 하는 경우를 생각해보자. 에디터로 100번 입력하는거 생각보다 짜증날 것이다. 그렇다고 프로그램을 만드는건 더 시간 낭비다. 다음과 같은 방법을 이용해 보자.
    esc키를 눌러서 명령행 모드로 넘어간다.
    100을 입력한다.
    'i'를 입력해서 입력모드로 들어간다.
    반복할 문자인 1을 입력한다.
    esc키를 눌러서 다시 명령행 모드로 넘어간다.
    1초 정도 기달려보자.
    

VIM을 사용하자

윤 상배

dreamyun@yahoo.co.kr

고친 과정
고침 0.92004년 3월 10일 17시
vim 모드와 관련된 부가내용 추가
고침 0.82003년 3월 1일 23시
최초 문서작성

1. vim 이란

vim 은 유닉스 계열에서 전통적으로 널리 사용도던 vi 의 improve 즉 undo, syntax coloring, split windows 등의 기능을 포함시킨 vi 의 보강된 프로그램이다.

이 문서는 vim 의 기본적인 사용법과, 프로그래밍을 위한 여러가지 팁을 담고 있다. vim 버젼은 6.0 을 기준으로 한다. vim(vi)에 대한 자세한 사용방법은 여기에서는 제시하지 않을것이다. 가장 기본적인 사항만 언급할것이며, 자세한 사용법은 vi 사용자그룹 사이트를 참고하기 바란다.


2. VIM의 기본사용법 익히기

이번장에서는 vim의 기본적인 사용법에 대해서 알아보도록 하겠다. 위에서 언급했듯이, 이문서는 VIM의 레퍼런스 가이드는 아니다. 기본적인 사용이 가능하도록 가장 기초적인 내용들만 다룰것이다.


2.1. vim 모드

다른 에디터를 사용하던 유저가 vim을 처음 접하면서 가장 난감해 하는 부분이 vim의 상태(mode)개념이다.vim은 다른 에디터들과 달리, 실행을 시켰다고 해서 즉시 입력이 이루어지지 않는다. 많은 vim을 처음 접하는 유저는 어떻게 글을 입력할지 몰라서 vim의 사용을 접게되는 경우가 발생하는데, 여기에 그 이유가 있다. vi 는 크게 세가지 상태로 나뉘어진다. 첫번째가 명령어 모드로 키입력이 바로 실행되는 상태이며, 다음은 상태모드로 실제 문서를 편집하는 모드 마지막이 ex 상태로 ex 명령을 실행시키는 상태이다.

vi 를 처음실행시키면 입력모드가 아닌 명령모드 상태에 놓이게 된다. 이 상태에서는 문자의 입력이 이루어지지 않으며, 찾기등과 같은 간단한 문서관련 명령을 실행할 수 있다. 이 명령모드 상태에서 ":" 키를 누르면 ex 상태로 a, i, o 키 등을 누르면 입력 상태로 넘어가게 된다. 일단 입력상태로 들어가게 되면 문서 편집을 할수 있게 되는데, 이때 ESC 키를 누르면 명령모드 상태로 넘어가게 된다.

표 1. vim의 상태(mode)

명령 상태처음 vim을 실행했을때, 입력상태/명령상태에서 ESC입력시간단한 찾기, 커서 이동, ex 상태로 넘어가기
ex 상태명령 상태에서 (":")각종 치환, 저장, 파일읽기, vim설정등과 같은 대부분의 작업들
입력 상태명령 상태에서 (a,i,o 키 입력)내용 입력


2.2. 명령어모드의 사용

우리는 명령모드에서 여러가지 명령을 입력함으로써, 복사, 붙이기, 삭제 문서입력, 문서저장, 문서불러오기, 커서이동 등의 작업을 할수 있다. 이번 장에서는 이러한 명령모드에서 사용되는 각종 명령어에 대해서 알아보도록 하겠다.


2.2.1. 커서 이동

기본적으로 vi 는 입력모드에서 방향키를 이용해서 커서 이동을 하지 못하도록 되어있다. 비록 최근의 vim 이 입력모드에서 방향키를 이용한 커서 이동을 지원하고 있기는 하지만, 명령모드에서의 키이동이 훨씬 빠르고 편하므로, 처음에는 좀 어색하더라도 명령모드에서의 키 이동을 익히도록 하자.

표 2. 커서 이동

k커서를 위로 움직임
j커서를 아래로 움직임
h커서를 왼쪽으로 움직임
l커서를 오른쪽으로 움직임
-커서를 줄의 처음으로 옮김
e, E다음단어의 끝으로, 문자단위 이동
w, W다음단어의 처음으로, 문자단위 이동
$줄의 마지막으로
0줄의 처음으로
^줄의 처음으로(공백이 아닌 처음시작되는 문자)
Shift+g문서의 마지막으로 이동한다.
gg, 1g문서의 처음으로 이동한다. 1대신 다른 숫자를 입력하면 해당 숫자의 라인으로 이동한다.
), (다음, 이전 문장의 처음으로
}, {다음, 이전문단의 처음으로
]], [[다음, 이전 구절의 처음으로


2.2.2. 화면 스크롤

위의 커서명령 이동이 매우 편하기는 하지만, 만약 페이지가 한 2000라인 될경우, 위의 커서를 이용해서 한줄씩 이동하는데에는 너무 많은 시간이 걸릴것이다. 그래서 vi 에서는 화면 단위의 스크롤이 가능한 명령들을 제공한다. 아래의 화면 스크롤 명령어들을 익히면 빠른 위치이동을 위해 매우 유용하게 사용할수 있다. ^F 는 CTRL+F 를 의미한다.

표 3. 화면 스크롤

^F한 화면 을 앞으로 스크롤
^B한 화면 을 뒤로 스크롤
^D반 화면 을 앞으로 스크롤
^U반 화면 을 뒤로 스크롤
^E한줄 앞으로 스크롤
^Y한줄 뒤로 스크롤
Shift + h화면의 맨 윗줄로
Shift + m화면의 중간줄로
Shift + l화면의 맨 아랫줄로


2.2.3. 마크 이동

일종의 책갈피 기능이라고 보면 된다. 자주 참조해야할 라인에 마크를 해놓고 필요할때 곧바로 마크된 영역으로 이동하기 위해서 사용한다. 마크는 mx 형식으로 사용할수 있다. x 는 (a~z)까지의 문자로 마크된 영역의 이름을 지정하기 위해서 사용한다. 마크된 영역으로 이동하기 위해서는 'x 와 `x 를 사용한다. 'x 는 마크된 라인의 가장 앞으로 이동하고, `x 는 마크된 라인의 정확한 위치로 이동한다.


2.2.4. 입력 명령

지금 vi 를 실행시켜보자. vi 는 기본적으로 명령모드로 실행되므로, 지금상태에서는 문서 작성을 할수 없을것이다. 우리는 다음과 같은 키입력을 통해서 입력모드 상태로 전환할수 있다.

표 4. 입력 명령

i현재위치에서 삽입
I현재줄의 처음위치에서 삽입
a현재위치에서 한칸앞으로 이동해서 삽입
A현재줄의 마지막위치에서 삽입
o새로운 줄을 커서 아래에 연다
O새로운 줄을 커서 위연다
s현재 위치의 문자를 지우고 입력모드로 들어간다.
S현재위치의 라인을 지우고 입력모드로 들어간다.


2.2.5. 편집명령

여기에서는 vi의 편집기능인 복사, 붙이기, 삭제에 대해서 알아 보도록 하겠다. 다른 에디터들은 보통 마우스를 이용해서 블럭을 지정해서 편집을 하는 반면, vi 는 명령어 모드에서 키보드 만을 이용해서 편집이 가능하므로, 매우 편리하고 빠르게 편집작업들이 가능하다. 또한 라인단위 블럭, 블럭단위 블럭등의 선택 모드를 지원함으로써, 문서에서 원하는 부분에 대한 작업을 좀더 쉽게 할수 있다.


2.2.5.1. 편집(none visual block 모드)

visual block 모드가 아닌 상태에서이 편집에 관한 내용이다.

표 5. 복사,삭제,붙이기

y한줄 복사
yn현재 라인에서부터 n라인만큼을 복사
p복사된 내용 붙이기
dd한줄삭제
dw한단어 삭제
Shift+d, d$현재커서 위치에서 마지막까지 삭제
Shift+j현재 행의 개행문자를 제거한다. 즉 아래라인을 현재라인에 덧붙인다.


2.2.5.2. Undo (되돌리기)

vim 은 다중의 undo 기능을 지원한다. 뒤로 되돌리고 싶다면 단지 'u'키만 입력하면 된다.


2.2.5.3. 블럭 지정

이번엔 블럭지정, 그중에서도 vim 에서 지원하는 visual 블럭 지정에 대해서 알아보겠다. vim visual 블럭 지정 기능을 사용할경우 지정범위가 반전되면서 눈에 보이기 때문에, 효과적인 블럭지정이 가능하도록 도와준다. 범위지정을 위해서 'hjkl', 'Shift+g,GG' 과 같은 이동명령 과 화면스크롤 명령을 사용해서 범위지정을 좀더 빠르게 할수 있다.

표 6. 블럭지정

v단어단위로 블럭지정이 가능하다. 블럭범위는 이동명령인 'hjkl' 로 원하는 범위 만큼 지정할수 있다.
Shift+v라인단위 블럭지정이다. 라인전체가 선택되며, 위아래 이동명령 'hj' 으로 범위 지정이 가능하다.
Ctrl+v블럭단위 블럭지정이다. 4각형의 블럭지정이 가능하며 이동명령인 'hjkl' 로 원하는 범위를 지정할수 있다.
Shift+v 와 같이 블럭지정을 한후 Shift+G 를 입력하면 현재라인부터 마지막 라인까지가 블럭 지정이 될것이다.


2.2.5.4. 편집(visual block 모드)

일단 vim 의 visual 블럭 지정 기능을 이용해서 편집하기 원하는 블럭을 지정했다면, 각종 편집명령을 이용해서 복사, 붙이기, 삭제 작업이 가능하다. 블럭을 지정한 상태에서 아래의 명령을 이용해서 편집을 하면 된다. 명령어는 기본적으로 none visual block 모드의 편집 명령어과 같다.

표 7. 편집(복사, 삭제, 붙이기)

y지정된 블럭을 복사한다.
p복사된 블럭을 현재라인(커서) 아래에 붙인다.
d지정된 블럭을 삭제한다.
dd현재라인을 삭제한다.


2.3. ex 모드

2.3.1. 찾기/치환

vim 의 기능중 가장편리한 기능으리면 뭐니뭐니 해도, 정규표현식을 이용한 강력한 찾기기능과 치환기능이라고 할수 있을것이다. 물론 다른 대부분의 에디터들도 찾기기능과 치환기능을 제공하긴 하지만, vim 의 기능은 다른 에디터들에 비해서 정말로 독보적인 편리함과 강력함을 제공한다. vi 사용자가 다른 에디터로 넘어가기 힘든이유중 가장큰 이유가, 바로 "키를 이용한 방향입력" 과 "찾기 및 치환" 기능 때문이다.

사실 찾기 치환의 기능을 제대로 이해하고 사용하기 위해서는 정규표현식(regular expression) 에 대한 이해가 필요로 하는데, 이것은 다음의 사이트를 참조하기 바란다. 정규 표현식의 간략한 소개

먼저 찾기 기능에 대해서 알아보겠다. 찾기기능은 ':/패턴/' 를 이용 하면된다. 찾기 원하는 문자혹은 패턴을 입력하고 엔터키를 누르면 현재 커서위치에서 가장 가까운 곳에 위치한 문자열로 커서를 이동시킨다(문서 아래방향으로). 다음 문자열을 찾기를 원한다면 'n'키를 누르면 된다. 문서에서 가장 마지막에 이르르게 되면, 문서의 가장처음부터 다시 찾기 시작한다. 'Shift+n' 을 이력하면 반대 방향(문서의 위쪽으로)으로 찾기를 시작한다.

치환이야 말로 vim 의 꽃이라고 할수 있다. :[범위]s/[oldpattern]/[newpattern]/ 의 형식으로 사용하면 된다. 범위 지정은 visual block 을 이용할수도 있으며, 직접 범위를 입력할수도 있다. visual block 를 이용한 치환은 visual block 를 지정한다음 ':' 를 입력해서 ex 모드로 넘어가면 된다. 그리고나서 ':'<,'>s/[oldpattern]/[newpattern/' 과 같은 방법으로 치환하면 된다.

visual block 를 사용하지 않고 직접범위를 입력할수도 있다. :[시작],[마지막]s/[old]/[new]/ 식으로 범위를 지정하면 된다. 여기에는 몇가지 지정된 범위를 위한 특수 기호들이 있다. '%' 는 전체문서(처음부터 끝까지), '.' 은 현재, '$' 은 마지막 을 나타낸다. 숫자를 입력할경우 숫자는 라인을 나타낸다. 다음은 간단한 사용예이다.

# 문서 처음부터 마지막까지의 char 를 _char_ 로 치환한다. 
:%s/char/_&_/g

# 현재(커서위치)부터 마지막까지의 char 를 _char_ 로 치환한다.
:.,$s/char/_&_/g

# buf_.*[255], buf_in[255], buf_get[255] 와 같은 문자열을 hello 로 변경한다.  
:1,10s/buf_.*\[255\]/hello/g
				
마지막에 쓰인 'g' 는 global 이다. 즉 해당 라인 전체에 걸쳐서 검색후 치환한다. 'g' 를 사용하지 않을경우 라인에서 처음에 검색된 문자만 치환하고 다음 라인으로 넘어간다.


2.3.2. 파일 저장, 열기, 종료

파일열기의 경우 vi 를 실행시킬대 명령행 옵션으로 열기가 가능하다. 또한 vi 를 이미 실행 시킨후에도 명령모드에서 명령을 입력함으로 파일을 열수 있다. 열고자 하는 파일이 이미 존재할경우에는 존재하는 파일이 열리고, 열고자 하는 파일이 존재하지 않을경우 새로운 파일이 만들어진다.

표 8. 저장,열기,종료

:e [filename]filename 으로 파일열기
:q, :q!, :wq종료, 강제종료, 저장후 종료
:w, :w [filename]현재파일명으로 저장, filename 로 저장
:<범위>w [filename]지정한 범위만 다른 파일로 저장
:e [filename]filename 을 편집하기 위해서 연다
ZZ지금파일을 저장하고 vim 을 종료한다.
:f현재 작업중인 파일의 이름과, 라인수를 출력한다

3. 개발자를 위한 vim 사용팁

3.1. 화면 나누기

vim 은 수평나누기와 수직나누기를 제공한다. 수평나누기는 ":split [파일이름]" 수직나누기는 "vs [파일이름]" 으로 나눌수 있다. 파일이름을 지정한 경우, 새로 만들어진 창에는 파일이름 을 가지는 파일이 열리고, 파일이름을 지정하지 않을경우 똑같은 파일이 열린다. 이 기능은 현재 파일의 다른 부분을 참조하고 싶을때 유용하게 사용할수 있다(참조하는 부분으로 이동하기 위해서 왔다갔다 하지 않아도 되므로). 또한 ":10split [파일이름]", "10vs [파일이름]" 등으로 창의 크기를 조절해 줄수도 있다. 창 나누기는 2개 이상 나누기도 가능하다.

이렇게 창을 분할시켜 놓으면 쏘쓰를 참조하기도 편하고, 무엇보다 편집(삭제,복사,붙이기)가 가능하므로 훨씬더 작업을 수월하게 할수 있다.


3.1.1. 화면 이동

명령 모드에서 CTRL+ww 를 입력하면 된다. 그러면 아래창으로 이동한다. 임의로 이동하기 위해서는 Ctrl+w 를 입력한 상태에서 이동명령[hjkl]를 이용하면 원하는 방향으로 창이동이 가능하다.


3.1.2. 파일 네비게이션

vim 6.0 부터는 파일네비게이션 기능이 존재합니다. 예를들어 vi 로 파일을 열때 파일을 디렉토리로 지정하면 해당디렉토리의 내용이 네비게이션 되고, 디렉토리 이동및 파일 선택이 가능하다.

	
vi ./   # 현재 디렉토리내용을 네비게이션 해준다. 
				


3.2. 파일 네비게이션 바 만들기

윈도우의 울트라 에디트와 같은 프로그램을 보면 왼쪽에 파일네비게이션이 있어서 원하는 파일을 바로 선택해서 편집하는 기능이 있다. vim 으로도 이러한 기능을 구현할수 있다. 이것은 vim 의 file navigation 기능과 창나누기 기능을 이용해서 구현하면 된다.

vi 가 실행되 상태에서 수직창 나누기 기능을 이용해서 ":20vs ./" 명령을 내려보자 그럼 그림과 같이 오른쪽에 파일 네비게이션 바가 생김을 알수 있다.

그림 1. 파일네비게이션을 만든 화면

이제 열기를 원하는 파일위치에 가서 shift+o 를 입력해보자, 그럼 옆의 편집창에 새로운 파일이 열리는것을 알수 잇을것이다. 여기에 더해서 편집장을 split 로 나누면, 여러개의 파일을 오가면서 편집이 가능해질 것이다. [vim 7에서는 shift+p로 단축키가 변경됐다]


3.3. 여러개의 파일 편집하기

위에서는 창나누기를 이용한 여러개의 파일편집에 대해서 알아봤는데, 또다른 방법이 있다. 처음에 vim 을 통하여 여러개의 파일을 open 하고 여러개의 열린 파일을 이동하면서 편집하는 방법이다. 먼저 vim을 다음과 같이 실행시킨다.

 
[yundream@localhost test]# vim file1.txt file2.txt ...
			
그러면 처음 화면은 file1.txt 편집화면일것이다. 2번째 파일인 file2.txt 편집화면으로 넘어가길 원한다면(앞에 있는 파일 편집)
:n
			
file2.txt 에서 file1.txt 를 편집하길 원한다면(뒤에 있는 파일편집)
:e#
			
split 를 이용해서 여러개의 파일을 편집할때와 마찬가지로, 각종 편집기능(복사,삭제,붙이기)이 서로 공유되므로 편하게 작업이 가능하다.


3.4. 잠시 쉘로 나가기

보통 vim상에서 쉘명령어를 실행시키기 위해서 :![명령어] 를 사용하는데, 이것 보다는 Ctrl+z 를 이용해서 쉘로 빠져나가서 작업하는게 더 편하다. shell 이 job control 기능을 이용한것으로, 쉘에서 원하는 작업을 수행하후 fg 명령을 이용해서 다시 vi 편집 상태로 되돌아 올수 있다. vim 사용자를 보면 가끔 쉘작업을 하기 위해서 vim 을 아예 종료 시켜서 쉘로 빠져나간 다음에 작업을 하고 vim 을 다시 실행시키는 경우가 있는데, 이제는 그럴필요가 없이 좀더 편하게 작업을 할수 있을것이다.


3.5. 선택된 block 를 다른 이름으로 저장하기

split 기능을 이용해서 창을 나누고, 원하는 블럭을 선택해서 복사한다음에, 새로만든창에 가져다 붙이기를 하면 된다.

그러나 이방법은 조금 복잡한 감이 없잖아 있다. 이럴때는 블럭을 선택해서 :'<,'>w [파일명] 하면 좀더 간단하게 원하는 작업을 수행할수 있다.


3.6. 빠른 괄호 이동

C나 C++ 을 사용하다보면 제어문이나 함수에서 많은 괄호('{','(')를 만나게 된다. 이때 괄호의 제일 마지막으로 이동하고 싶을때가 있을것이다. 이럴때는 ']}' 를 사용하면 된다. '[{' 를 사용하면 괄호의 처음으로 이동한다.


3.7. 위치 마크(mark)하기

일종의 북마크기능으로 자주참조할만한 라인을 마킹해두고 필요할때 간단히 해당 마킹지역으로 이동하기 위해서 사용한다. 마킹을 위해서는 명령모드에서 m키를 눌러서 마킹모드로 들어가면 된다. 그리고 영문 [a-zA-Z]키중 아무거나 눌러주면 된다. 만약 a를 눌러주었다면, 현재라인은 a이름으로 마킹된다. 이후 작업을하다가 a마킹라인으로 가고 싶다면 'a 해주면된다. 이 상태에서 원래 라인으로 되돌아가고 싶다면 ''를 눌려주면 된다.

물론 다중마킹도 허용한다. 마킹할수 있는 문자는 단일영문자이다. 마킹에 사용되는 영문자는 대소문자를 구분함으로 최대마킹가능한 수는 27*2가 될것이다.


3.8. 폴더(접기) 기능이용하기

vim 6.0 에 새로이 포함된 좋은 기능으로 코드의 특정영역을 접을수 있다. 그럼으로 코드를 분석할때 쓸데 없는 부분을 감춰줘서 좀더 편하게 분석이 가능합니다. visual block 를 이용해서 원하는 영역을 선택한다음 :zf 를 이용하면 해당영역이 접힌다. :zo 를 사용하면 접힌영영을 원상태로 복구할수 있고 :zc 를 사용하면 해당영역을 다시 접을수 있다. 또한 다중 접기를 허용해서 접근구역을 다시 접을수도 있다.


3.9. 간단한 man 페이지 참조

vim 을 이용 코딩중에 함수의 프로토 타입이 생각나지 않을때 주로 man page 를 참조하게 된다. 보통은 창을 하나따로 띄워서 그곳에서 man page 를 보는데, 코딩중에 간단하게 해당 함수에 대한 man page 를 볼수 있다. man page 를 원하는 함수 위로 커서를 옮긴다음 Shift + k 를 입력하면 함수의 man page 가 뜰것이다. 'q' 를 입력해서 man page 를 종료시키면 원래의 vim 화면으로 되돌아온다.


3.10. 함수/변수명 자동완성

코딩중에 가장 범하기 쉬운 잘못중의 하나가 변수명및 함수명 오타일것이다. 또 변수명이 기억이 잘 나지 않아서 처음 선언한곳을 다시 확인하는 작업역시 코딩을 매우 번거롭게 한다. 이때 함수 자동완성 기능을 이용하면 이러한 염려들을 줄일수 있다.

int client_sockfd 라고 변수 선언을 했다고 하자. 코딩중에 client_sockfd 를 쓸일이 있다면 cli^p 를 입력해보자. 그러면 변수 이름이 자동으로 완성되는것을 볼수 있을것이다. ^p는 Ctrl+p 이다.


3.11. ctags 를 이용한 쏘쓰 분석

쏘쓰를 분석하는데 있어서 가장 중요한 것은 역시 함수를 분석해서, 함수가 어떤일을 하는지 알아내는 것이다. ctags 를 이용하면 이러한 쏘쓰 분석작업을 좀더 수월하게 할수 있다. ctags 와 관련된문서는 ctags 를 이용한 쏘쓰 분석 을 참고하기 바란다.


3.12. 자동들여쓰기

프로그래밍 할때 indent 는 쏘쓰코드를 보기좋게 만들기 위한 필수 사항이다. 보통 tab 을 주로 쓰는데,

:set ai
			
명령을 이용하면 자동적으로 indent (auto indent) 를 적용시켜주므로, 좀더 코딩에만 집중할수 있도록 도와준다.
:set noai 
			
명령을 사용해서 auto indent 상태를 해제할수 있다.

요즘의 vim 은 기본적으로 auto indent 상태이므로, 별다른 설정없이 편하게 사용가능하다. 그러나 웹에서 가져다 붙이기를 할때 여기에 auto indent 가 적용되어서 걷잡을 수 없이 tab 이 들어가는 경우가 생길때도 있는데, 이럴때 set noai 를 이용해서 auto indent 를 해제하고 가져다 붙이기를 하면 된다.


3.13. 탭사이즈 조정하기

쏘쓰에서 indent 를 위해서 주로 탭을 사용하는데, 보통 이 탭 사이즈는 8로 되어 있다. 그런데 8이란 탭사이즈가 때로는 너무 커서, 쏘쓰가 화면밖으로 나가서 오히려 쏘쓰 보기를 어렵게 만들때도 있다. 이럴때 는 탭사이즈를 줄여야 하는데 다음과 같은 명령을 통해서 탭사이즈 변경이 가능하다.

:set ts=4
			


3.14. 라인 넘버링

코딩하다보면 라인넘버가 있으면 할때가 있다. 그럴때는

:set nu 
			
하면 된다.

그림 2. 라인 넘버링

라인넘버를 없애고 싶다면,
:set nonu
			
하면 된다.


3.15. 코드를 HTML로 저장하기

vim 은 또한 코드를 HTML 형태로 저장하는 기능을 가지고 있다. 이 기능을 이용하면 syntax highlight 된 상태 그대로 HTML로 변환이 가능하다. 쏘쓰코드의 예제를 만들어서 웹상에 올리고자 할때 유용하게 사용할수 있는 기능이다.

:so $VIMRUNTIME/syntax/2html.vim 
			


3.16. vim 설정파일 만들기

지금까지 우리는 다양한 설정을 통해서 vim 을 좀더 쉽게 사용하는 방법을 알아 보았다. 그런데, 탭사이즈를 적용하기 위해서 vim 을 실행시킬때 마다 ":set ts=4" 이런식으로 하면 작업이 매우 귀찮을것이다. 이럴때는 vim 을 위한 설정파일을 만들어서, vim 이 시작할때 설정파일을 읽어들여서 환경이 자동으로 설정되도록 하면된다.

자기의 계정(Home) 디렉토리에 보면, .vimrc 라는 파일이 존재 할것이다. (존재하지 않는다면 만들도록한다) 이것이 설정파일로 아래와 같은 방법으로 자기가 원하는 내용을 설정하면 된다.

set ts=4
set nu
			

Tag :  
category_TOOL
category__14


저작자 표시 비영리 변경 금지
신고
Software Architect/LINUX


링크 : http://idkwim.tistory.com/66

저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/LINUX

http://soodiy.com/266

저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/LINUX

출처 : http://mcchae.egloos.com/10937303

 



VMWare Fusion에 설치를 하고 ISO는 "CentOS-6.3-x86_64-minimal.iso 입니다.

첫번째 메뉴를 선택했습니다.

우분투에는 없는 RedHat 계열의 이 메시지를 오랫만에 보는군요. Skip을 눌렀습니다. (가상머신이잖아요 ^^)

설치 환경은 GUI 환경이네요, Next

Minimal이라 그런지 한글을 선택했더니 깨지더군요. 그냥 영문으로 설치했습니다.
나중에 데스크탑으로 올리면서 별도의 한글 추가 설치가 가능합니다.

설치 버전에 이런 확장스토리지 지원하는 것은 우분투 보다 앞선듯 합니다.

새롭게 설치할 것이므로, Yes

호스트 이름을 넣고, Next

처음 미국으로 되어 있는데, 서울로 수정하고 Next

우분투인 경우에는 보안을 위하여 root가 아닌 wheel 그룹의 sudo 가능한 사용자를 만들어 사용하는데,
이쪽 계열은 rootㄹ 바로 들어간다는 것이 큰 차이 중에 하나네요.
개발 때는 root로 디버깅하는 것을 생각하면 뭐 이것도 편한 장점 중에 하나입니다.
Next

뭐 전체사용하고, Next

이제 설치를 하는데, 우분투 서버랑 설치 시간은 비슷한듯 (아니면 약간 더 빠른듯...)

설치를 마두리 합니다. Reboot

처음 부팅하면서 하단에 이렇게 올라가는 것이 깔끔해 보이네요~

설치된 크기를 확인하니 700메가 정도네요... 이거면 거의 우분투의 JEOS (최소가상머신설치) 버전에 버금가네요~
훌륭합니다.

제일 먼저 네트워크 설정방법입니다.

# vi /etc/sysconfig/network-scripts/ifcfg-eth0
IPADDR="1.2.3.4"
NETMASK="255.255.255.0"
GATEWAY="1.2.3.1"
DNS1=168.126.63.1
DNS2=168.126.63.2
DOMAIN=test.co.kr

위의 내용을 추가한 다음,

# service network restart

로 네트워크 서비스만 재 기동시키면 됩니다.


이제 미니멀 설치의 마지막으로 데몬을 정리해 봅니다.

ssh 데몬은 디폴트로 기동됩니다.

# yum install ntsysv

ntsysv를 설치하는 장면입니다.

저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/LINUX

출처 : http://igusl.com/igusl/?p=83

 

  • CentOS를 설치하기 위한 준비물

 

[CentOS 설치 이미지]

http://centos.org

Downloads – CentOS Public Mirror List -

South American, Asian, Oceania, Middle Eastern, African and Other Regional Mirrors

의 경로로 접속해 보면 CentOS를 다운 받을 수 있는 아시아 공식 서버 리스트가 보인다.

물론 다른 나라 서버에서도 동일한 이미지 파일을 다운받을 수 있지만

상대적으로 느릴 수 있으므로 한국 서버를 이용한다.

 

Asia – Korea 서버 리스트 중 아무거나로 결정한 후 맨 오른쪽의

HTTP, FTP, RSYNC 이렇게 세 개의 링크 중 FPT를 선택한다.

32비트 컴퓨터에 설치하려면 [파일 리스트/가장 최신버전/isos/i386],

64비트 컴퓨터에 설치하려면 [파일 리스트/가장 최신 버전/isos/x86_64]의 경로로 접속한다.

CentOS-[버전]-[i386 or x86_64]-netinstall.iso 파일을 다운 받는다.

 

 

[데스크탑] + [DVD_ROM or 256MB 이상의 USB 메모리스틱]

아무 컴퓨터나 상관 없으니 쓰지 않아 굴러다니는 컴퓨터나 노트북을 활용하면 좋다.

DVD-ROM이 없으면 다운 받은 CentOS-[버전]-[i386 or x86_64]-netinstall.iso 파일로

부팅 가능한 USB를 만든다.

 

DVD-ROM이 있으면 netinstall이 아닌

CentOS-[버전]-[i386 or x86_64]-bin-DVD1.iso

CentOS-[버전]-[i386 or x86_64]-bin-DVD2.iso

를 다운받아 DVD에 구워 설치한다.

 

 

 

  • CentOS 설치

 

다운 받은 이미지로 부팅.

 

CentOS 6 Install 001

Install or upgrade an existing system 선택.

 

 

CentOS 6 Install 002

설치 이미지가 유효한지 테스트. 시간이 걸리므로 Skip 해도 무방하다.

 

 

CentOS 6 Install 003

설치 시의 언어 선택.

 

 

CentOS 6 Install 004

설치 시 text mode에서는 한글을 지원하지 않는다는 메시지.

설치 시에만 한글이 안나오고 설치 후 한글 설정할 수 있으니 그냥 OK 선택.

 

 

CentOS 6 Install 005

리스트 중 ko를 찾아 선택. us를 선택해도 설치 후 변경할 수 있으니 상관 없다.

 

 

CentOS 6 Install 006

이 포스트에서는 netinstall 기준으로 설치할 예정이니 URL 선택.

DVD-ROM으로 부팅했다면 Local CD/DVD 선택.

설치에 필요한 파일 다운로드 하냐마냐의 차이만 있을 뿐 설치 과정은 같다.

 

 

CentOS 6 Install 007

일반 가정 인터넷을 사용하면 위 설정 그대로,

고정 IP를 가지고 있다면 IPv4 manual configuration에서 IP 설정.

 

 

CentOS 6 Install 008

netinstall 시 위와 같이 설치 이미지 경로만 적어주면 된다.

위에서 설명한 설치 이미지 mirror list 중 CDNetworks 기준

ftp://centos.mirror.cdnetworks.com/centos/6.4/os/x86_64/images/install.img

를 적어준다.

 

 

CentOS 6 Install 009

CentOS 설치에 필요한 이미지 다운로드.

 

 

CentOS 6 Install 010

OK 선택.

 

 

CentOS 6 Install 011

새로 사거나 초기화 되지 않은 드라이브에 설치하려 한다면 위와 같은 메시지가 뜨는데,

Re-initialize를 해주면 된다.

주의할 점은 설치될 드라이브가 아닌 다른 드라이브를 초기화 시켜버릴 수 있으므로

설치 전 다른 드라이브가 연결되어 있다면 전부 제거하고,

설치완료 후 다시 연결하는게 좋다.

 

 

CentOS 6 Install 012

해당 시간대의 도시 선택. 한국은 Asia/Seoul.

 

 

CentOS 6 Install 013

시스템 최고권한을 갖는 root 계정의 비밀번호 설정.

설정 후 아무에게도 알려주면 안된다.

 

 

CentOS 6 Install 014

설정한 비밀번호가 강력하지 않다는 메시지.

좀더 강력하게 설정하려면 Cancel 선택 후 재설정.

입력한 비밀번호를 그냥 사용하려면 Use Anyway 선택.

 

 

CentOS 6 Install 015

파티션 나누지 않고 디스크 전체를 사용하려면 Use entire drive 선택.

리눅스 파티션에 대한 이해가 조금이라도 있는 사용자라면

Replace existing Linux system을 선택해 파티션 나눠 사용하는 것을 권장.

/, /boot, /var, 나머지 전체 공간 정도는 나누어 놓는게 좋다.

 

 

CentOS 6 Install 016

Write changes to disk 선택.

 

 

CentOS 6 Install 017

패키지 의존성 체크. 자동으로 넘어간다.

 

 

CentOS 6 Install 018

OS 설치 중.

 

 

CentOS 6 Install 019

패키지 설치 중.

 

 

CentOS 6 Install 020

설치되었으면 재부팅.

 

 

CentOS 6 Install 021

부팅 중.

 

 

CentOS 6 Install 022

부팅 완료.

데스크탑 패키지를로 설치했다면 X Window 화면이 뜨겠지만 기본은 쉘 모드로 부팅됨.

 

사실 쉘 원격접속으로 모든 서비스 제어가 가능하기에

X Window가 반드시 필요한 것은 아니지만,

X Window 시스템을 이용하면 서버에서 공유기 설정이 쉽다는 점,

여러 개의 쉘을 한 화면에 띄워 놓고 작업할 수 있다는 점,

인터넷에서 자료를 찾아보고 바로 적용할 수 있다는 점 등등 여러 장점이 있다.

X Window를 이용할 수 있는 데스크탑 패키지를 설치해 보자.

 

최초 부팅 시 root 계정 로그인 후

yum groupinstall “Desktop” “Desktop Platform” “X Window System”

입력 후 엔터.

 

스크린샷 2013-04-20 오후 7.52.35

y 엔터. 하면 설치가 된다.

 

설치가 완료되면 한글 폰트를 설치 해 준다.

yum install fornts-korean

입력 후 엔터.

한글 폰트가 설치되었다면 startx 엔터를 입력해 X Window를 시작해 본다.

 

스크린샷 2013-04-12 오전 1.32.06

이제 X Window를 사용할 수 있게 되었다.

하지만, 재부팅 하면 다시 쉘 모드로 부팅되므로 X Window로 부팅되도록 설정하자.

 

쉘에서

vi /etc/inittab

하면 맨 아래 id:3:initdefault: 라고 되어 있을 것이다.

3을 5로 id:5:initdefault: 이렇게 바꿔 저장해 주면 다음 부팅 때부터

X Window로 부팅된다.

저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/LINUX

Oracle Database는 Oracle에서 개발한 관계형 Database로 금융권과 일반 기업, 공공기관, 심지어 우리나라에서는 초등학교 학내전산망에까지 사용됩니다. (초등학교에 얼마나 많은 DB 처리량이 필요하길래.. 대표적인 예산 낭비 행정이죠. ㅎㅎ Mysql로도 충분한 것을..) 뛰어난 안정성과 Performance로 가장 널리 사용되는 Database인 Oracle은 Windows, Linux, Unix 계열 Server에서 사용할 수 있습니다.

Oracle Database에서 공식적으로 지원하는 Linux 배포판은 Asianux Server, Red Hat Enterprise Linux(RHEL), SUSE Linux Enterprise Server입니다. 최신 Release인 Oracle 11g Release 2에서 지원하는 배포판을 자세히 열거하면 아래와 같습니다.

  • Asianux Server 3 SP2
  • Oracle Enterprise Linux 4 Update 7
  • Oracle Enterprise Linux 5 Update 2
  • Red Hat Enterprise Linux 4 Update 7
  • Red Hat Enterprise Linux 5 Update 2
  • SUSE Linux Enterprise Server 10 SP2
  • SUSE Linux Enterprise Server 11

물론 공식적으로 지원하지 않는 Linux 배포판이라도 설치 및 구동이 가능합니다. 이번 Post에서는 Ubuntu에서 Oracle 11g Relase 2를 설치하는 방법을 알아보도록 하겠습니다.

1. 필수 Package 설치

Oracle Database를 설치하기 위해서 사용되는 필수 Package를 설치합니다.

1.1. Natty Narwhal 11.04 이하의 배포판의 경우

Terminal에서 다음과 같이 입력합니다.

1
sudo apt-get install unzip build-essential x11-utils rpm ksh lsb-rpm libaio1

1.2. Oneiric Ocelot 11.10의 경우

Terminal에서 아래의 명령으로 설치합니다.

  • 32bit:
    1
    2
    3
    sudo apt-get install unzip build-essential x11-utils rpm ksh libaio1
    wget "http://launchpadlibrarian.net/64397544/lsb-rpm_4.8.1-6ubuntu1_i386.deb"
    sudo dpkg -i lsb-rpm_4.8.1-6ubuntu1_i386.deb
  • 64bit:
    1
    2
    3
    sudo apt-get install unzip build-essential x11-utils rpm ksh libaio1
    wget "http://launchpadlibrarian.net/64397680/lsb-rpm_4.8.1-6ubuntu1_amd64.deb"
    sudo dpkg -i lsb-rpm_4.8.1-6ubuntu1_amd64.deb

1.3. Precise Pangolin 12.04의 경우

Terminal에서 다음과 같이 입럭합니다.

  • 32bit:
    1
    2
    3
    4
    sudo apt-get install unzip build-essential x11-utils rpm ksh libaio1 libdb4.8
    wget "http://launchpadlibrarian.net/59032965/liblzma2_5.0.0-2_i386.deb"
    wget "http://launchpadlibrarian.net/64397544/lsb-rpm_4.8.1-6ubuntu1_i386.deb"
    sudo dpkg -i liblzma2_5.0.0-2_i386.deb lsb-rpm_4.8.1-6ubuntu1_i386.deb
  • 64bit:
    1
    2
    3
    4
    sudo apt-get install unzip build-essential x11-utils rpm ksh libaio1 libdb4.8
    wget "http://launchpadlibrarian.net/59033014/liblzma2_5.0.0-2_amd64.deb"
    wget "http://launchpadlibrarian.net/64397680/lsb-rpm_4.8.1-6ubuntu1_amd64.deb"
    sudo dpkg -i liblzma2_5.0.0-2_amd64.deb lsb-rpm_4.8.1-6ubuntu1_amd64.deb

1.4. Quantal Quetzal 12.10의 경우

Terminal에서 다음과 같이 입럭합니다.

  • 32bit:
    1
    2
    3
    4
    5
    sudo apt-get install unzip build-essential x11-utils rpm ksh libaio1
    wget "http://launchpadlibrarian.net/100815189/libdb4.8_4.8.30-11ubuntu1_i386.deb"
    wget "http://launchpadlibrarian.net/59032965/liblzma2_5.0.0-2_i386.deb"
    wget "http://launchpadlibrarian.net/64397544/lsb-rpm_4.8.1-6ubuntu1_i386.deb"
    sudo dpkg -i libdb4.8_4.8.30-11ubuntu1_i386.deb liblzma2_5.0.0-2_i386.deb lsb-rpm_4.8.1-6ubuntu1_i386.deb
  • 64bit:
    1
    2
    3
    4
    5
    sudo apt-get install unzip build-essential x11-utils rpm ksh libaio1
    wget "http://launchpadlibrarian.net/100815432/libdb4.8_4.8.30-11ubuntu1_amd64.deb"
    wget "http://launchpadlibrarian.net/59033014/liblzma2_5.0.0-2_amd64.deb"
    wget "http://launchpadlibrarian.net/64397680/lsb-rpm_4.8.1-6ubuntu1_amd64.deb"
    sudo dpkg -i libdb4.8_4.8.30-11ubuntu1_amd64.deb liblzma2_5.0.0-2_amd64.deb lsb-rpm_4.8.1-6ubuntu1_amd64.deb

2. libstdc++5 설치

libstdc++ Package는 GNU Standard C++ Library로서 GCC와 같은 GNU의 Compiler로 Compile한 Application을 위한 Runtime Library입니다.

Ubuntu의 경우, libstdc++6가 설치되어 있는데 Oracle Database에서는 libstdc++5를 사용합니다.

2.1. Lucid Lynx 10.04의 경우

Repository에서 libstdc++5를 지원하지 않으므로 다음과 같은 방법으로 libstdc++5를 설치합니다.

  • 64bit OS일 경우
    1
    2
    3
    4
    wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-3.3/libstdc++5_3.3.6-17ubuntu1_amd64.deb
    $ dpkg-deb -x libstdc++5_3.3.6-17ubuntu1_amd64.deb libstdc++
    sudo cp libstdc++/usr/lib/libstdc++.so.5.0.7 /usr/lib/
    sudo ln -s /usr/lib/libstdc++.so.5.0.7 /usr/lib/libstdc++.so.5
  • 32bit OS일 경우
    1
    2
    3
    4
    wget http://security.ubuntu.com/ubuntu/pool/universe/i/ia32-libs/ia32-libs_2.7ubuntu6.1_amd64.deb
    $ dpkg-deb -x ia32-libs_2.7ubuntu6.1_amd64.deb ia32-libs
    sudo cp ia32-libs/usr/lib/libstdc++.so.5.0.7 /usr/lib/
    sudo ln -s /usr/lib/libstdc++.so.5.0.7 /usr/lib/libstdc++.so.5

2.2. Maverick Meerkat 10.10 이후의 배포판의 경우

Quantal Quetzal 12.10, Precise Pangolin 12.04, Oneiric Ocelot 11.10, Natty Narwhal 11.04, Maverick Meerkat 10.10의 경우에는 다음과 같이 입력하면 간단히 설치됩니다.

1
sudo apt-get install libstdc++5

3. Group과 계정 생성하기

Oracle을 설치하고 사용하기 위한 Group과 계정을 생성합니다. 다음과 같이 Terminal에서 입력합니다.

1
2
3
4
5
sudo addgroup oinstall
sudo addgroup dba
sudo useradd -g oinstall -G dba -d /home/oracle -s /bin/bash -m oracle
sudo passwd oracle
sudo chown oracle:dba /home/oracle -R

4. Symbolic Link 설정하기

설치 Program에서 사용하는 일부 Program의 경로가 달라서 문제가 발생할 수 있기 때문에 설치 Program이 인식할 수 있는 Directory로 Symbolic Link를 걸어줍니다.

1
2
3
sudo ln -s /usr/bin/awk /bin/awk
sudo ln -s /usr/bin/rpm /bin/rpm
sudo ln -s /usr/bin/basename /bin/basename

5. Kernel 변수 설정

Oracle Database는 DB의 안정성과 Performance를 위해서 System의 자원을 많이 사용합니다. 그래서 기본적인 Linux Kernel 설정으로는 DB를 운용하는데 문제가 있기 때문에 다음과 같이 Kernel 변수를 설정해 줍니다.

Ubuntu에서는 Kernel 변수를 설정하는 File을 /etc/sysctl.d에 관련된 Application 별로 저장하고 적용할 수 있습니다. 이렇게 설정해 놓으면 차후 배포판이 Upgrade되거나 관련 Package가 Update될 때 설정이 변경되는 문제를 해결할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo vi /etc/sysctl.d/30-oracle.conf

fs.file-max = 6815744
fs.aio-max-nr = 1048576
kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
net.core.rmem_default = 4194304
net.core.rmem_max = 4194304
net.core.wmem_default = 1048576
net.core.wmem_max = 1048576
net.ipv4.ip_local_port_range = 9000 65535

sudo service procps start

6. Resource Limit 설정

Oracle DB를 위해서 System의 보안정책으로 묶여있는 자원설정을 변경합니다. oracle 계정에 한하여 최대 Process 수와 최대 열 수 있는 File Descripter 수를 설정합니다.

Ubuntu에서는 /etc/security/limits.d 에서 각 Application에 대한 Limit 설정을 할 수 있습니다. Terminal에서 다음과 같이 입력합니다.

1
2
3
4
5
6
sudo vi /etc/security/limits.d/oracle.conf

oracle soft nproc 2047
oracle hard nproc 16383
oracle soft nofile 1023
oracle hard nofile 65535

7. 설치 Directory 생성

Oracle Database가 설치될 Directory를 미리 생성합니다. Oracle 계정에서 해당 Directory를 생성하지 못하기 때문에 Root 권한으로 미리 만들어 놓습니다.

1
2
3
4
sudo mkdir /opt/oracle
sudo chown oracle:dba /opt/oracle
sudo mkdir /opt/oraInventory
sudo chown oracle:oinstall /opt/oraInventory

8. oracle 계정을 위한 환경변수 설정

이제 System 설정의 마지막입니다. Home Directory의 .bashrc에 마지막 부분에 생성한 oracle 계정에 적용할 환경변수를 추가합니다.

1
2
3
4
5
6
7
8
su - oracle
vi ~/.bashrc

export ORACLE_BASE=/opt/oracle
export ORACLE_HOME=$ORACLE_BASE/product/11.2.0/db_1
export ORACLE_SID=orcl11
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib
export PATH=$PATH:$ORACLE_HOME/bin

9. 마치면서..

Oracle Database를 설치하기 위한 기본적인 설정 및 필수 Package 설치는 모두 마쳤습니다. 이제 Oracle을 Download 받고 Installer를 사용해서 설치하는 것과 Ubuntu에서 편리하게 Oracle을 사용하기 위한 설정들을 하면 설치는 마무리 될 것입니다. 다음 Post에서는 Oracle을 Installer를 사용해서 설치하는 방법을 알아보겠습니다.

Changelog

  • Ubuntu Quantal Quetzal 12.10에서 Test 완료. (2012-11-07)
  • Ubuntu Precise Pangolin 12.04에서 Test 완료. (2012-05-21)
  • Ubuntu Oneiric Ocelot 11.10에서 Test 완료. (2011-12-08)
  • Ubuntu Natty Narwhal(11.04)에서 Test 완료. (2011-07-07)
  • Ubuntu Maverick Meerkat(10.10)에서 Test 완료. (2010-11-10)
  • Ubuntu Lucid Lynx(10.04)에서 Test 완료.
출처 : http://ioriy2k.pe.kr/archives/656

저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/LINUX

[펌] 우분투에 오라클 설치

 출처 : http://pmguda.com/727  http://ssppmm.tistory.com/1568 

Oracle 11g 설치

1. 오라클 11g를 설치하기 이전에 필요한 패키지들을 설치 하도록 하자. 
   (아래 작업들은 root계정으로 실행하도록 하자.)

# apt-get install gcc binutils libaio1 lesstif2 lesstif2-dev make rpm libc6 build-essential gawk alien ksh libmotif3

2. 패키지의 설치가 끝났으면 오라클 설치를 위한 사용자 그룹과 유저를 만들도록 한다.

# addgroup oinstall
# addgroup dba
addgroup nobody
# useradd -m oracle -g oinstall -G dba /bin/bash
# usermod -g nobody nobody
# passwd oracle  (oracle 비밀번호 설정)

3. 오라클에서 필요한 메모리와 CPU 리소스를 위해 커널 파라미터를 수정한다.
    파일의 가장 아래쪽에 다음의 내용을 삽입한다.

# vi /etc/sysctl.conf

fs.file-max = 65535
= 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 32000 100 128
net.ipv4.ip_local_port_range = 1024 65000
net.core.rmem_default = 1048576
= 1048576
net.core.wmem_default = 262144
net.core.wmem_max = 262144

4. /etc/sysctl.conf 파일은 우분투 시스템이 부팅시 처음  읽어오는 정보이다. 
   이를 적용하기 위해 sysctl.conf 설정을 읽어들이는 명령어를 실행한다.

# sysctl -p

5. /etc/security/limits.conf 파일에 다음 내용을 삽입한다.

/etc/security/limits.conf

oracle soft nproc 2047
oracle hard nproc 16383
soft nofile 1023
oracle hard nofile 65535

6. /etc/pam.d/login 파일에 다음 라인을 추가한다.

# vi /etc/pam.d/login

session required /lib/security/pam_limits.so
session required pam_limits.so

7. 오라클 기동에 관련한 환경변수를 설정한다.

# ln -s /usr/bin/awk /bin/awk
# ln -s /usr/bin/rpm /bin/rpm
# ln -s /lib/libgcc_s.so.1 /lib/libgcc_s.so
# ln -s /usr/bin/basename /bin/basename /bin/basename

8. 오라클 11g가 설치될 폴더 생성하기

# mkdir /oracle
# mkdir /oracle/11g
# chown -R oracle:oinstall /oracle
# chmod -R 775 /oracle

9. 환경변수 설정을 위해 /etc/profile 의 파일 가장 마지막에 삽입한다.

# vi /etc/profile

export ORACLE_BASE=/oracle
export ORACLE_HOME=/oracle/11g
export ORACLE_SID=oracle
export PATH=$PATH:$ORACLE_HOME/bin

10. 환경변수가 제대로 등록되었는지 확인해보도록 한다.

# su - oracle
password:
# echo $ORACLE_BASE
/oracle
# echo $ORACLE_HOME
/oracle/11g
# echo $ORACLE_SID
oracle

제대로 출력되는 것을 확인 하면 이상무..

11. 오라클11g 압축 풀기.(오라클을 오라클 계정에 다운받도록 한다.)
# su - oracle
$ unzip linux_11gR1b5_database.zip - 파일명은 버전에 따라 달라질달라질 수 있습니다. 
$ chown -R oracle:oinstall database

이제 오라클 설치의 기본적인 것은 완료 되었다. GUI 환경에서 설치를 진행하면 된다.
서버의 화면에서 설치를 진행해도 되고 본인같은 경우는 XDMCP 환경에서 설치를 진행하였다.
XDMCP 설정관련은 본인의 블로그에 작성되어 있다. 필요하면 참고 하도록 하자.
http://www.pmguda.com/726    <== XDMCP 설정관련 자료

12. 오라클 설치는 오라클 계정으로 하도록 하자.
# su - oracle
$ export LANC=C  //한글로 설치하면 글자가 깨짐으로 영어로 설치를 진행한다.
$ cd /home/oracle/database
$ ./runInstaller

아래 그림에서 보시는 것처럼
Oracle Base Location: /oracle
Oracle Home Location: /oracle/11g
Global DataBase Name: oracle
(다른 값을 넣을 경우에는 위에서 설정한 "export ORACLE_SID=oracle" 와 동일하게 설정을 맞춰주도록 하자. )


아래 창이 뜨기 전에 경고창이 하나 떳을 것이다 Inventory directory 가 잘못되있다는 경고창이니 
무시하고 설정을 변경해 주도록 하자.
Inventory directory 는 /oracle/oraInventory로 설정
System group name 은 dba로 설정

나머지 과정은 Next로 설치를 진행하면 된다. 아래 Product-Specific Prerequisite Cheks 에서 오류항목은
User Verified로 체크하여 처리를 하자. 

참고로 자신의 glibc 버전확인은 # getconf GNU_LIBC_VERSION   으로 하면 확인가능.
(glibc 관련 오류는 우분투 10.04의 버전이 더 높으나 비교에서 문제가 발생하는것이다. 
최소 요구버전은 2.4.?? 이라면 우분투(10.04)의 버전은 2.11.1 인데 비교시에 4와 11을 
4와 1로 비교하여 발생하는 버그이니 그냥 무시하도록 하는것이다. 오라클 개발자 신경좀 쓰자잉~)

오라클 관리담당자 등록부분이다 그냥 Next로 넘어가자


계속 Next로 설치를 진행하고 나면 다음 창이 나온다.
Scripts to be executed: 에 있는 2가지 스크립트 파일을 반드시 root 계정으로 실행하도록 한다. 


설치가 완료되었다. 이제 마지막 설치 후 셋팅 부분이 남았다.

13. /etc/oratab 파일 을 수정하도록 한다.(오라클 계정으로 빨간색 Y부분이 원래 N으로 되어있다.)

$ vi /etc/oratab

oracle:/oracle/11g:Y

14. /oracle/11g/bin/dbstart 파일의 오라클 리스너의 절대 패스를 수정한다.
ORACLE_HOME_LISTNER= 부분을 ORACLE_HOME_LISTNER=/oracle/11gORACLE_HOME_LISTNER=/oracle/11g 로 설정

$ vi /oracle/11g/bin/dbstart

15. 절대 패스까지 설정했다면 이제 설치는 완료 됬다. 확인 및 오라클 자동 실행을 위한 셋팅을 알아보자.

oracle 계정으로 전환해서 정상 작동 테스트를 해보자.
$sqlplus /nolog
SQL> connect /as sysdba
SQL> startup 
SQL> SELECT LTRIM('AAATEST','A') FROM DUAL;
SQL> TEST
SQL> shutdown

정상작동이 확인된 후에 자동실행 되도록 셋팅하도록 하자.(모든 작업은 root 권한으로) /etc/init.d 에서   startoracle.sh 파일을 생성

# vi /etc/init.d/startoracle.sh


01.#!/bin/sh
02.### BEGIN INIT INFO
03.# Provides:          startoracle
04.# Required-Start:    checkroot
05.# Required-Stop:    
06.# Default-Start:   
07.# Default-Stop:   
08.# Short-Description: Start with daemon at boot time
09.# Description:       Oracle auto start Script
10.### END INIT INFO
11.su - oracle -c /oracle/11g/bin/"lsntctl start"
12.su - oracle -c /oracle/11g/bin/dbstart


위와 같이 작성하고 실행 권한을 주도록 하자
1.#chmod 755 startoracle.sh
2. 
3.#update-rc.d startoracle.sh defaults

내용작성후 실행권한을 부여하고 시작프로세스에 등록한다.  재시작하면 자동 실행 된다.

저작자 표시 비영리 변경 금지
신고
0 0
1
블로그 이미지

Software Architect

DaddyGom

티스토리 툴바