지난번에 제출하신 두개의 소스코드 과제 별로 컴파일을 수행할 수 있는 makefile을 제출해주세요.
소스코드 별로 디렉토리를 만들어서 (src1과 src2) 해당 디렉토리별로 소스코드를 저장하고 이를 컴파일하기 위한 makefile을 압축하여 제출해주세요.
수업에서 배운 mk2 예제를 참조해서 작성해주세요.
세 번째 과제는 makefile을 작성하는 것이다.
첫 번째 과제의 소스 코드 read.c
와 두 번째 과제의 소스 코드 my_cp.c
를 사용하여 이번 과제를 수행한다.
read.c
와 my_cp.c
는 각각 gcc -o read read.c
와 gcc -o my_cp my_cp.c
명령으로 컴파일했다. 이는 직접 명령어로 프로그램을 컴파일하는 방법으로 즉시 실행이 가능하며 간단한 수정 후 바로 컴파일할 수 있다.
하지만 각각의 컴파일 명령어를 기억해야 하기 때문에 복잡한 프로젝트에서는 관리가 어렵고 수정하지 않은 파일도 다시 컴파일하게 될 수도 있는 단점이 존재한다.
Makefile은 위와 같은 컴파일 명령을 자동화한 형태다. 자동으로 빌드 과정을 관리하기 때문에 소스 파일이 많거나 복잡한 프로젝트에서도 자동으로 컴파일이 가능하며 어떤 파일이 변경되었는지를 추적하여 수정된 파일만 다시 컴파일할 수 있다.
이번 과제에서는 두 개의 프로그램을 각각의 Makefile로 컴파일을 자동화하여 빌드 과정을 관리한다.
Projects/
├── src1/
│ ├── read.c
│ ├── test.txt
│ └── read_mk
└── src2/
├── my_cp.c
├── src_file
├── dst_file
└── cp_mk
위 구조를 토대로 코드를 작성한다.
mkdir src1
mkdir src2
이전에 작성했던 두 개의 소스코드는 Projects
라는 프로젝트 디렉토리에 위치해 있으므로 read.c
와 my_cp.c
를 방금 생성한 src1
, src2
로 각각 이동시킨다.
mv read.c src1
mv my_cp.c src2
read.c
read.c 소스 코드는 test.txt를 읽고 출력하는 프로그램이다. 따라서 실행하려면 test.txt 파일이 작업 디렉토리에 위치해 있어야 하기 때문에 마찬가지로 src1에 이동시킨다.
mv test.txt src1
my_cp.c
my_cp.c 소스 코드는 사용자로부터 복사할 파일(src_file)과 복사 후 생성될 파일(dst_file)을 전달받아 실행하는 복사 프로그램이다. 따라서 실행하려면 src_file과 dst_file 파일이 작업 디렉토리에 위치해 있어야 하기 때문에 src2에 이동시킨다.
mv src_file src2
mv dst_file src2
read.c
makefile 생성 후 작성vim read_mk
CC = gcc
CFLAGS = -Wall
SRC = read.c
TARGET = read
$(TARGET): $(SRC)
$(CC) $(CFLAGS) -o $(TARGET) $(SRC)
CC = gcc : 컴파일러로 GCC 사용
CFLAGS = -Wall : 컴파일 시 모든 경고 활성화
SRC = read.c : 컴파일할 소스 파일은 read.c
TARGET = read : 컴파일 후 생성될 실행 파일 이름은 read
$(TARGET): $(SRC) : SRC(read.c)에 의존하는 TARGET(read)을 생성
$(CC) $(CFLAGS) -o $(TARGET) $(SRC) : gcc 컴파일러를 사용하여 read.c를 컴파일하고 결과물을 read라는 이름의 실행 파일로 출력
👀 의존하다? 의존성이란?
Makefile에서 “의존한다”는 것은 타겟(Target)을 생성하기 위해 필요한 파일들을 의미하며, 의존성 파일이 변경되었을 때만 다시 컴파일한다.→ "SRC(read.c)에 의존하는 TARGET(read)"은 read 실행 파일을 생성하려면 read.c 소스 파일이 필요하다는 뜻
빌드 전 src1 디렉토리를 ls
명령어로 검사한 결과 맨 처음 작성한 디렉토리 구조와 동일하게 모든 파일이 위치해 있는 것을 확인할 수 있다.
(빌드 후 실행을 이미 한 상태에서 캡처를 해 버려서 사진 상에는 실행 파일이 이미 존재한다 ㅎ)
my_cp.c
makefile 생성 후 작성vim cp_mk
CC = gcc
CFLAGS = -Wall
SRC = my_cp.c
TARGET = my_cp
$(TARGET): $(SRC)
$(CC) $(CFLAGS) -o $(TARGET) $(SRC)
빌드 전 src2 디렉토리 또한 ls
명령어로 검사한 결과 맨 처음 작성한 디렉토리 구조와 동일하게 모든 파일이 위치해 있는 것을 확인할 수 있다.
read_mk
빌드make -f read_mk
직접 명령어 gcc -o read read.c
와 동일하게 컴파일되었다.
cp_mk
빌드make -f cp_mk
직접 명령어 gcc -o my_cp my_cp.c
와 동일하게 컴파일되었다.
👀 -Wall은 왜 추가됐지?
→ 아까 Makefile에 CFLAGS = -Wall을 추가했기 때문
-Wall
옵션은 소스 코드를 컴파일할 때 일반적인 경고 메시지(warning messages)를 출력하며, 이 옵션을 사용하면 잠재적인 문제를 미리 발견하고 수정할 수 있는 장점이 있다.
위 사진은 Makefile의 read_mk
파일을 빌드하면서 발생한 구문 오류(Syntax error)이다. &(CC)
부분에서 & 기호가 잘못 사용되었고, 이를 셸이 인식하지 못하면서 구문 오류가 발생했다.
이렇게 구문 오류는 직접적으로 컴파일을 실패하게 만들지만 -Wall 옵션은 코드 내에서 발생할 수 있는 경고를 출력해 줌으로써 수정할 수 있도록 도와준다.
read_mk 파일이 성공적으로 빌드되어서 컴파일에 성공하고 잘 실행되는지를 확인하기 위해 test.txt의 내용을 확인한다.
cat test.txt
Hello World!
가 출력된다. 만약 생성된 read 파일이 잘 실행된다면 같은 텍스트가 출력될 것이다.
./read
test.txt와 동일하게 Hello World!
가 출력되는 것을 보아 정상적으로 동작하는 것을 확인할 수 있다.
cp_mk 파일이 성공적으로 빌드되어서 컴파일에 성공하고 잘 실행되는지를 확인하기 위해 src_file과 dst_file의 내용을 확인한다.
cat src_file
cat dst_file
각각 다른 텍스트가 출력되는 것을 확인할 수 있다. 만약 my_cp 파일이 잘 실행된다면 dst_file의 내용도 src_file과 같은 This is Test
가 출력되어야 한다.
./my_cp src_file dst_file
두 파일 모두 This is Test
가 출력되는 것을 보아 정상적으로 동작하는 것을 확인할 수 있다.
scp -P 2022 -r pu2rile@서버 주소:/home/pu2rile/Projects/src1 .
scp -P 2022 -r pu2rile@서버 주소:/home/pu2rile/Projects/src2 .
해당 코드로 원격 디렉토리 카피 후 src1와 src2를 압축해서 제출한다.
끝.