2장 매크로
https://flower0.tistory.com/459
위 내용에 이어서 설명하고자 한다.
이번 포스팅에서 설명할 내용을 맛보기로 보자면, 매크로를 사용하여 다음과 같이 코드 개선이 가능하다.
매크로 정의 방법
name = text string // 선언
$(name) // 사용방법
${name}
name은 text string으로 치환된다.
참고로, 문자 하나로 이루어진 매크로 이름에는 괄호나 중괄호를 사용할 필요가 없다.
A = XYZ
$A
$(A)
${A}
위와 같이 사용하면 A는 XYZ로 치환된다.
예시1
// 매크로 정의
LIBS = -lx11
Objs = drawable.o plot_points.o root_data.o
CC = /usr/fred/cc
23 = "This is the (23)rd run"
OPT =
DEBUG_FLAG = #empty now, but assign -g for debugging
BINDIR = /usr/local/bin
// 매크로 사용
Plot: ${objs}
${CC} -o plot ${DEBUG_FLAG} ${objs} ${LIBS}
mv plot ${BINDIR}
make plot을 실행하면 명령은 다음과 같이 실행된다.
/usr/fred/cc -o plot drawable.o plot_points.o root_data.o -lx11
mv plot /usr/local/bin
예시2
SOURCE = ${MY_SRC} ${SHARED_SRC}
MY_SRC = parse.c search_file.c
SHARED_DIR = /usrs/b_proj/src
SHARED_SRC = ${SHARED_DIR}/depend.c
make 는 ${SOURCES}를 아래와 같이 평가한다.
parse.c search_file.c /users/b_proj/src/depend.c
위의 예시를 보면 알겠지만, 매크로를 정의하는 순서는 상관없다.
예시3
TEST_MACRO = test
TEST_MACRO = test2
all:
@echo "${TEST_MACRO}";
make를 하면 결과로 'test2'가 출력된다.
즉, make는 항상 파일에서 마지막 정의만을 참조로 사용한다.
내장 매크로
make에서 내부적으로 정의된 매크로들이 있다.
${CC} : C컴파일러. '$cc' 명령과 같다.
${CXX} : CPP 컴파일러. '$g++' 명령과 같다.
${LD} : 링커
${RM} : 삭제 명령어
이 외에도 아래 링크에서 더 확인할 수 있다.
https://www.tutorialspoint.com/makefile/makefile_macros.htm
내부에서 정의된 모든 매크로들을 표시하는 방법
$make -p
make에서 p옵션을 주면 다음과 같이 정의된 매크로들을 모두 보여준다.
명령 행에서 매크로 정의
// Makefile
all:
@echo "${TEST_MACRO}";
makefile을 위와 같이 정의하였고, 명령행에서 매크로를 정의하여 전달할 수 있다.
$ make TEST_MACRO=test3
결과는 test3을 출력한다.
혹은 make 명령 이전에 매크로를 정의할 수도 있다. (본 셸과 콘셸에서만 가능)
$ TEST_MACRO=test3 make
위와 동일하게 test3을 출력한다.
셸 변수
변수 정의를 셸에서 입력한다면 다음과 같이 사용할 수 있다.
$ TEST_MACRO=test4; export TEST_MACRO
$ make
결과는 test4를 출력한다.
참고로 $export 명령을 통해 정의해놓은 변수를 확인할 수 있다. 그리고 시스템을 재부팅하면 사라진다.
(위의 예제는 본셸과 콘셸에서만 사용이 가능하고, C셸 사용자는 setenv 명령을 사용해야 한다.)
매크로 할당 우선순위
예를 들어, DIR 매크로를 makefile 안에서는 /usr/proj로 정의해두고, 현재 셸 환경에서는 /usr/proj/lib으로 정의해두었을 경우, make는 실제로 둘 가운데 어떤 정의를 사용할까?
make는 아래와 같은 우선순위를 매겨 사용한다.
1)make 명령 입력 시 make 명령 다음에 입력한 매크로
2)makefile의 매크로 정의
3)현재 셸 환경 변수.
여기에는 사용자가 make 명령 행에 입력할 때 명령 바로 앞에 입력한 매크로도 포함된다. (본셸과 콘셸에만 해당)
4.make의 내장(기본) 정의
위의 우선순위는 다음과 같이 테스트를 해볼 수 있다.
// makefile
TEST_MACRO = insideMake
all:
@echo "${TEST_MACRO}";
// 환경변수 정의
$ TEST_MACRO="environment variable"; export TEST_MACRO
// make 명령어와 함께 매크로 정의
$ make TEST_MACRO="command macro"
결과는 "command macro"를 출력한다.
그러나 여기에서 make 명령어 앞에 매크로 정의를 한다면 결과는 달라진다.
$ TEST_MACRO="command macro" make
결과는 insideMake를 출력한다.
억지로 환경 변수에 매크로를 우선적으로 참고하는 방법
e옵션을 주면 makefile보다 환경변수의 매크로를 우선적으로 참고한다.
$ make -e
위 명령어에 따라 바뀐 우선순위는 아래와 같다.
1) make 명령 입력 시 make 명령 다음에 입력한 매크로
2) 현재 셸 환경 변수.
여기에는 make 명령행에 입력할 때 명령 바로 앞에 입력한 매크로도 포함된다.(본셸과 콘셸만)
3)기술 파일의 매크로 정의
4)makefile의 매크로 저으이
5)make의 내장(기본) 정의
make 밖으로 나와 셸 스크립트를 활용하는 방법 - 래퍼 스크립트
위와 같이 매크로가 여러가지 방법으로 선언될 수 있기 때문에 래퍼 스크립트를 활용하여 매크로를 확인하여 make를 실행시키는 방법이 있다. 이 방법은 현업에서 유용하게 사용된다.
//make_local.sh
if
TEST z "$PROJECT_TREE"
then
PROJECT_TREE=usr/local; export PROJECT_TREE
fi
make $*
사용자가 PROJECT_TREE를 정의했는지 확인한다. 정의했다면 스크립트는 아무런 조처없이 종료한다.
반면 정의되지 않은 경우, PROJECT_TREE는 사용자가 선택한 기본값을 취한다.
마지막 줄이 make를 실행시키면서 명령 행에서 모든 인수들을 전달한다.
make_local 실행시키기
$ make_local source_stream
매크로 문자열 치환
예제1
SRC = def.s redraw.c calc.c #매크로 정의
ls ${SRC:.c=.o} # 기술 파일 명령 내리면
calc.o defs.o redraw.o # 마치 이들 파일들이 존재하는 것처럼 결과를 출력한다.
make는 {SRC}를 평가하고, 콜론 뒤에 따라오는 문자열을 찾은 다음 등호 기호 뒤의 문자열을 치환한다.
이 기능을 이용하면 확장자만 또는 마지막에 첨부된 문자만 다른 여러 파일로 이뤄진 그룹들을 쉽게 관리할 수 있다.
예제2
SRC = defs.c redraw.c calc.c
FINAL_OBJS = ${SRC:.c=.o}
DEBUG_OBJS = ${SRC:.c=_dbg.o}
문자열 치환 예외 상황
LITTER = xyz xyzabc abcxyz
echo ${LITTER:xyz=DEF}
결과는 다음과 같이 출력한다.
DEF xyzabc abcDEF
문자열 치환은 매크로의 마지막 부분이나 공백 문자 바로 앞까지만 적용이 가능하기 때문이다.
내부적으로 정의되어 있는 macro
$@ : 현재 target의 이름 (명령행에서만 의미를 지닌다)
$$@ : 현재 target의 이름 (종속행에서만 의미를 지닌다)
$< : 현재 target 보다 최근에 변경된 필요 항목 리스트 (확장자 규칙에서만 사용가능)
$* : 현재 target보다 최근에 변경된 현재 필요 항목의 이름 (확장자 제외). (확장자 규칙에서만 사용 가능).
$% : 현재의 타깃이 라이브러리 모듈일 때 .o 파일에 대응되는 이름
예제1.
CMDS = cat dd echo date cccmp comm ar ld chown
${CMDS} : $$@.c
${CC} -O $? -o $@
예제2.
/temp/target : /usr/local/c/test.c
${@F} : 현재 target의 파일 부분 (target)
${<F} : 현재 필요 항목의 파일 부분 (test.c)
${@D} : 현재 target의 디렉토리 부분 (/temp)
${<D} : 현재 필요 항목의 디렉토리 부분 (/usr/local/c)
매크로 활용 예제