42 June - 5. So_Long (개념)

수현·2023년 6월 11일
0

42Seoul

목록 보기
6/8

📒 Technical consideration

when

  • 23.06.16 20:30 ~ 21:30
  • 23.06.17 22:00 ~ 24:00
  • 23.06.21 19:30 ~ 01:00
  • 23.07.01 19:30 ~ 05:00
  • 23.07.02 11:00 ~ 17:00
  • 23.07.05 19:00 ~ 02:20
  • 23.07.08 13:15 ~ 17:30
  • 23.07.12 20:00 ~ 02:30
내용
제출 파일Makefile, .h, .c, maps, textures
MakefileNAME, all, clean, fclean, re
외부 함수open, close, read, write, malloc, free, perror, strerror, exit + MinilibX 라이브러리 내 모든 함수, math library
매개변수읽어들일 파일의 descriptor (서술자)
인자\*.ber 형태의 맵
libft 사용허용

📌 주의사항

  • norm error 금지

  • segmetation fault, bus error, double free 금지

  • heap에 동적 할당된 메모리 해제 (메모리 누수 방지)

  • Makefile 제출

    • -Wall -Wextra -Werror 플래그 지정
    • relink 금지 (다시 make했을 때 재실행 금지)
    • $(NAME), all, clean, fclean, re 규칙 포함
  • libft 사용

    • 소스와 Makefile을 libft 폴더에 복사해야함
    • 프로젝트 Makefile은 libft의 Makefile을 사용하여 라이브러리를 컴파일한 다음, 프로젝트 컴파일 해야함
  • MiniLibX 사용

    • 운영체제에서 이용 가능한 라이브러리와 과제에서 제공되는 소스 이용

📒 Mandatory part

📕 so_long

과제 이름이 so_long 이유

  • <은하수를 여행하는 히치하이커를 위한 안내서>
    • 초공간 이동용 우회로를 건설하려는 우주인들로 인해 지구가 파괴될 위험에 빠짐
    • 영화 속 설정으로 지능이 사람보다 높은 돌고래들은, 인간들에게 위험을 경고하려고 시도함
    • 하지만 인간들은 이 모습이 묘기부리는 모습으로 해석되어 경고를 받아들이지 못하고 돌고래들만 떠남
    • 돌고래의 마지막 메시지는 뒤로 세번 공중제비를 돌아 고리를 통과하면서 '성조기여 영원하라(The Star-Spangled Banner)'를 휘파람으로 부는 모습인데, 해당 메시지는 '안녕히, 그리고 물고기는 고마웠어요(So Long, And Thanks For All The Fish)'라는 뜻
  • Subject에서 돌고래가 물고기를 먹고 출구로 가는 모습을 예시로 듦

📕 Game Rule

(1) Game

  • 플레이어의 목표 : 모든 수집품을 모으고 최소한의 움직임으로 맵을 탈출하는 것
  • 조작 방식 : W, A, S, D 키를 이용 (상, 하, 좌, 우)
  • 게임 규칙
    • 플레이어는 벽을 뚫고 지나갈 수 없음
    • 각 움직임마다 현재까지 움직인 횟수가 쉘에 출력
    • 게임은 2D 시점(탑뷰 또는 프로필)으로 제작
    • 실시간으로 진행되는 게임일 필요는 없음

(2) Graphic Management

  • 이미지
    • MiniLibX의 images를 사용
    • 프로그램은 이미지를 화면에 표시
    • 작업 창 관리는 부드럽게 동작 (창 최소화, 다른 창으로 전환 등의 동작)
  • 종료
    -ESC : 창을 닫고 게임을 정상적으로 종료
    -좌상단의 빨간 버튼 (mac) : 창을 닫고 게임 정상 종료

(3) Map

  • 지도 구성 요소 : 벽, 수집품, 빈 공간
    • 0은 빈공간
    • 1은 벽
    • C는 수집품
    • E는 맵의 출구
    • P는 주인공의 시작지점
  • 지도 예시
1111111111111
10010000000C1
1000011111001
1P0011E000001
1111111111111
  • 지도 규칙
    • 출구 1개, 시작 지점 1개, 최소한 하나의 수집품 포함
    • 지도에 여러개의 캐릭터(출구 / 시작지점)를 가지고 있으면, 에러 메세지를 출력
    • 지도는 직사각형 모양
    • 지도는 벽으로 둘러싸여 있어야 합니다. 그렇지 않으면 에러를 반환
    • 지도에 유효한 경로가 있는지 확인
    • 위 규칙들을 준수한 모든 종류의 지도를 파싱할 수 있음
    • 지도에서 에러 발생하면, "Error\n" + 직접 정한 에러 메시지를 출력 후 종료
  • .ber 지도 예시
1111111111111111111111111111111111
1E0000000000000C00000C000000000001
1010010100100000101001000000010101
1010010010101010001001000000010101
1P0000000C00C0000000000000000000C1
1111111111111111111111111111111111

📕 miniLibX

1) minilibx 개념

  • X-Window 및 Cocoa에 대한 지식 없이도 화면에서 무언가를 그리기위한 기본적인 그래픽 라이브러리
  • 간단한 창 생성, 그리기 도구, 이미지 기능 및 이벤트 관리 시스템 제공

2) miniLibX 종류

(1) 초기화

#include <mlx.h>

void *mlx_init ();
  • 소프트웨어와 디스플레이간 연결 초기화
    • 연결 실패 시 : null
    • 연결 성공 시 : 포인터

(2) Window 관리

void *mlx_new_window (void *mlx_ptr, int size_x, int size_y, char *title);
  • 새로운 window를 스크린 위에 생성
    • mlx_ptr : 연결 식별자
    • size_x, size_y : 창 사이즈
    • title : 창 상단바 부분에 표시할 이름
    • 창 생성 실패 시 : null
    • 창 생성 성공 시 : void *win_ptr
int mlx_clear_window (void *mlx_ptr, void *win_ptr);
  • 창을 검은색으로 초기화 (기존에 그려놨던 윈도우 지워줌)
    • mlx_ptr : 연결 식별자
    • win_ptr : 창 사용 식별자
int mlx_destroy_window (void *mlx_ptr, void *win_ptr);
  • 창을 삭제하는 함수
    • mlx_ptr : 연결 식별자
    • win_ptr : 창 사용 식별자

(3) 드로잉

int mlx_pixel_put (void *mlx_ptr, void *win_ptr, int x, int y, int color);
  • 지정된 픽셀을 그리는 함수
    • x, y : 창의 x, y 좌표값
    • color : 000 - 255 사이의 값 [ 투명도 | R | G | B / 0x00RRGGBB ]
int mlx_string_put (void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string);
  • 지정된 문자열을 그리는 함수
    • string : 표시할 문자열

(4) 이미지

void *mlx_xpm_file_to_image(void *mlx_ptr, char *filename, int *width, int *height);
  • xpm 파일을 불러와 이미지로 변환하고 저장
  • 파일 또는 데이터를 받아 새로운 이미지를 생성해주는 함수
    • 이미지 생성 실패 시 : null
    • 이미지 생성 성공 시 : void *img_ptr
int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y);
  • 새로운 좌표를 이용한 새 위치에 이미지를 그려줌
  • xpm에서 이미지로 변환한 img를 화면에 (x, y) 위치에 띄워줌
    • img_ptr : 사용할 이미지 식별자
    • win_ptr : 창 식별자
    • x, y : 이미지가 위치할 창 내의 x, y좌표
char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);
  • 생성된 이미지 정보 반환 및 이미지 수정
    • 000 - 255 사이의 값 [ 투명도 | R | G | B / 0x00RRGGBB ]
    • bpp : 픽셀 하나(색상 하나)를 표현하는 데 필요한 비트 수
int *mlx_destroy_image(void *mlx_ptr, void *img_ptr);
  • 생성된 이미지 삭제

(5) 이벤트 처리

int mlx_loop(void *mlx_ptr);
  • 키보드나 마우스로부터 이벤트를 받기 위해 이벤트 수신하는 함수 (무한 루프)
  • 이벤트가 발생할 때 까지 기다린 다음 해당 이벤트와 연결된 사용자 정의 함수를 호출하는 무한 루프
int mlx_key_hook(void *win_ptr, int x_event, int x_mask, int (*funct)(), void *param);
  • 이벤트 처리 함수를 종합적으로 사용 (mlx_key_hook, mlx_mouse_hook, mlx_expose_hook)
  • 여러 종료의 입력을 받아 함수 실행
  • hook는 이벤트가 일어날 때 마다 call되는 함수
    • x_event : 발생한 이벤트 코드값 -> 밑에 사이트에서 X11의 "X.h" 부분에 정의되어 있다.
    • x_mask : MacOS에서는 사용하지 않는 값. 0으로 넣어서 안쓰는 것을 명시할 수 있도록 하자.
    • x, y : 이미지가 위치할 창 내의 x, y좌표
mlx_loop_hook(par.mlx, &draw, &par);
  • 이미지를 지우고 다시 그리는 draw함수를 이벤트마다 실행

📖 참고 📖 X-Window

X-Window는 유닉스 용 네트워크 지향 그래픽 시스템이다. 원격 데스크톱에 연결할 때 사용된다. 이러한 구현의 가장 일반적인 것은 TeamViewer이다.

📖 참고 📖 X11 Events

02: KeyPress
03: KeyRelease
04: ButtonPress
05: ButtonRelease
06: MotionNotify
07: EnterNotify
08: LeaveNotify
09: FocusIn
10: FocusOut
11: KeymapNotify
12: Expose
13: GraphicsExpose
14: NoExpose
15: VisibilityNotify
16: CreateNotify
17: DestroyNotify
18: UnmapNotify
19: MapNotify
20: MapRequest
21: ReparentNotify
22: ConfigureNotify
23: ConfigureRequest
24: GravityNotify
25: ResizeRequest
26: CirculateNotify
27: CirculateRequest
28: PropertyNotify
29: SelectionClear
30: SelectionRequest
31: SelectionNotify
32: ColormapNotify
33: ClientMessage
34: MappingNotify
35: GenericEvent
36: LASTEvent

📖 참고 📖 X11 masks

  • 이벤트가 trigger될 때 한가지 키만 등록할 수 있고, 모든 키 등록 가능
  • 키 마스크는 이벤트 목록으로부터 whitelist/blacklist 나눌 수 있음


    NoEventMask 0L
    KeyPressMask (1L<<0)
    KeyReleaseMask (1L<<1)
    ButtonPressMask (1L<<2)
    ButtonReleaseMask (1L<<3)
    EnterWindowMask (1L<<4)
    LeaveWindowMask (1L<<5)
    PointerMotionMask (1L<<6)
    PointerMotionHintMask (1L<<7)
    Button1MotionMask (1L<<8)
    Button2MotionMask (1L<<9)
    Button3MotionMask (1L<<10)
    Button4MotionMask (1L<<11)
    Button5MotionMask (1L<<12)
    ButtonMotionMask (1L<<13)
    KeymapStateMask (1L<<14)
    ExposureMask (1L<<15)
    VisibilityChangeMask (1L<<16)
    StructureNotifyMask (1L<<17)
    ResizeRedirectMask (1L<<18)
    SubstructureNotifyMask (1L<<19)
    SubstructureRedirectMask (1L<<20)
    FocusChangeMask (1L<<21)
    PropertyChangeMask (1L<<22)
    ColormapChangeMask (1L<<23)
    OwnerGrabButtonMask (1L<<24)

3) 실습

(1) window 생성

#include "mlx/mlx.h"
/
int	main(void) {
	void	*mlx;
	void	*win;
    /
	mlx = mlx_init();
	win = mlx_new_window(mlx, 500, 500, "so_long");
	mlx_loop(mlx);
}

(2) 키보드 입력받고 출력

#include <stdio.h>
#include <stdlib.h>
#include "mlx/mlx.h"
/
#define KEY_ESC				53
#define KEY_W				13
#define KEY_S				1
#define	PRESS_RED_BUTTON	17
/
typedef struct s_param{
	int		x;
	int		y;
}				t_param;
/
void	param_init(t_param *pa)
{
	pa->x = 3;
	pa->y = 4;
}
/
int	key_press(int keycode, t_param *pa)
{
	if (keycode == KEY_W)
		pa->x++;
	else if (keycode == KEY_S)
		pa->x--;
	else if (keycode == KEY_ESC)
		exit(0);
	printf("x: %d\n", pa->x);
	return (0);
}
/
int	close(void)
{
	exit(0);
	return (0);
}
/
int	main(void)
{
	void		*mlx;
	void		*win;
	t_param		pa;
/
	param_init(&pa);
	mlx = mlx_init();
	win = mlx_new_window(mlx, 500, 500, "key test");
	printf("-------------------------------\n");
	printf("Start x = 3\n");
	mlx_key_hook(win, &key_press, &pa);
    mlx_hook(win, PRESS_RED_BUTTON, 0, &close, &pa);
	mlx_loop(mlx);
}

(3) 이미지 출력

#include <stdio.h>
#include "mlx/mlx.h"
/
int	main(void)
{
	void	*mlx;
	void	*win;
	void	*img;
	int		img_width;
	int		img_height;
/
	mlx = mlx_init();
	win = mlx_new_window(mlx, 500, 500, "putImage");
	img = mlx_xpm_file_to_image(mlx, "img/mario.xpm", &img_width, &img_height);
	mlx_put_image_to_window(mlx, win, img, 50, 50);
	mlx_loop(mlx);
	return (0);
}

4) 이미지 움직이기

#include <stdio.h>
#include "mlx/mlx.h"
/
typedef struct s_param
{
	void	*mlx;
	void	*win;
	void	*c;
	int		fd;
	int		play_x;
	int		play_y;
	int		img_x;
	int		img_y;
	int		win_x;
	int		win_y;
}	t_param;
/
# define UP_W				13
# define DOWN_S				1
# define LEFT_A				0
# define RIGHT_D			2
# define EXIT_ESC			53
# define EXIT_BUTTON		17
/
int	key_press(int keycode, t_param *p)
{
	if (keycode == UP_W && p->play_y != 0)
		p->play_y -= p->img_y;
	if (keycode == DOWN_S && p->play_y != 480)
		p->play_y += p->img_y;
	else if (keycode == LEFT_A && p->play_x != 0)
		p->play_x -= p->img_x;
	else if (keycode == RIGHT_D && p->play_x != 480)
		p->play_x += p->img_x;
	else if (keycode == EXIT_ESC)
		exit(0);
	else if (keycode == EXIT_BUTTON)
		exit(0);
	printf("x: %d y : %d\n", p->play_x, p->play_y);
	return (0);
}
/
int	draw(t_param *p)
{
	mlx_clear_window(p->mlx, p->win);
	mlx_put_image_to_window(p->mlx, p->win, p->c, p->play_x, p->play_y);
	return (0);
}
/
int	main(void)
{
	t_param		par;
/
	par.mlx = mlx_init();
	par.c = mlx_xpm_file_to_image(par.mlx, "img/mario.xpm", &par.img_x, &par.img_y);
	par.win = mlx_new_window(par.mlx, 1000, 1000, "DrawMap");
	par.play_x = 0;
	par.play_y = 0;
	mlx_key_hook(par.win, &key_press, &par);
	mlx_loop_hook(par.mlx, &draw, &par);
	mlx_loop(par.mlx);
	return (0);
}

📕 .ber 확장자

개념

  • BER(Basic Encoding Rules)
  • 데이터 인코딩하는 방법

📕 xpm 파일

1) 개념

  • X11 Pixmap Graphic
    • X windows에서 사용되는 비트맵 이미지 (png -> xpm 변환)
    • ASCII text에 대응되는 색상이 define (단색 이미지 나타냄)
    • pixel로 ASCII text 표현됨
  • pixmap을 C 프로그래밍 언어의 정적 문자 배열로 저장

2) xmp 변환

  • mlx_png_file_to_image함수 자체의 구현에서 메모리 누수 등 문제 발생 (png -> image 변환)
  • 색연필 선택 -> 이미지 드래그 -> crop
  • 네모박스 선택 -> pixel (width/height 지정)
  • png -> xpm

📕 perror

void perror(const char *s);
  • 사용자 메시지 + 오류 메시지를 출력한다.

  • 전역 변수 errno 의 값을 해석하여 이에 해당하는 시스템 오류 메세지를 표준 오류 출력 스트림(stderr) 에 출력한다.

  • 헤더 : stdio.h

  • 매개변수

    • s : 시스템 오류 메세지 앞에 출력할 사용자 정의 메시지
  • 반환값

    • 없음

📕 strerror

char *strerror(int errnum);
  • 오류 메시지를 리턴
  • 헤더 : string.h
  • 매개변수
    • errnum : 오류 번호
  • 반환값
    • 오류 번호에 해당하는 오류 문자열을 가리키는 포인터

📕 경로 유효성 확인

1) 전체 탐색 알고리즘

  • 시작 지점과 도착 지점이 주어짐
  • 탈출 가능한 경로 출력 및 최단거리 확인 가능
  • DFS
    • 스택, 재귀 이용
  • BFS
    • 큐, 반복문 이용
  • 특징
    • 모든 노드를 방문하는 경우에 사용
    • 재귀 함수 사용시 종료 조건 명확하게 명시 필요
  • 과정
    • 하나의 경로를 끝까지 확인한 후 다시 이전으로 돌아와서 다음 경로 확인
    • 모든 장소를 확인할 때까지 반복해서 동작
  • 진행 순서
    • 1) 상하좌우를 살피며 이동할 수 있는지 확인
    • 2) 이동할 수 있으면 해당 공간으로 이동
    • 3) 1 ~ 2 반복, 더 이상 갈 수 없으면 경로에서 빠져나옴
    • 4) 1 ~ 3 반복, 탈출구 찾기
      const offset =
      [
      	[0, 1],  // 상
      	[0, -1], // 하
      	[-1, 0]  // 좌
      	[1, 0]   // 우
      ]
      void find_path(maps, visited, pos)
      {
      	if (pos == [4, 4]) // 종료 조건
      		return ;
      	else
      	{
      		visited[pos] = true; // 현재 위치를 방문한 것으로 표시
      		stack.push(pos);     // 현재 위치를 스택에 넣기
      		for (i = 0; i < 4; i++) // 상하좌우 이동 여부 탐색
      		{
      			next = pos + offset[i]; // 다음 좌표 저장
      			if (is_movable(maps, next) // 이동 가능 여부 판단
      				find_path(maps, visited, next);  // 다음 경로로 이동
      		}
      		stack.pop(pos); // 탐색에 실패하면 스택에서 현재 위치 제거
      	}
      }

📖 참고 📖 백트래킹 (backtracking)

  • 개념
    • 진행중인 경로가 아니면 더이상 경로를 가지 않고 되돌아가며 해를 찾는 과정
  • 목적
    • 불필요한 탐색을 줄이고, 올바른 쪽으로 동작
    • 모든 가능한 경우의 수 중에서 특정한 조건을 만족하는 경우만 보기

📕 leaks 검사

1) 개념

  • 프로세스에 할당된 가상 메모리 공간(malloc 사용) 중 더이상 reference되지 않는 영역 찾는 macOS 운영체제의 커맨드 라인 도구
  • 제공되는 정보
    • 메모리 누수가 발생한 영역의 주소
    • 누수된 영역의 바이트 단위 크기
    • 누수된 영역이 담고있는 데이터

2) 사용 방법

  • #include <stdlib.h>
  • system 함수
    • 기존의 프로그램 무한루프 -> 프로그램 실행 -> leaks a.out 대신 system("leaks a.out") 사용
  • atexit 함수
    • 다른 함수에서 exit 하는 경우 main문 마지막의 system 함수가 호출되지 않을 경우 사용
    • atexit의 인자로 호출하고 싶은 함수 포인터 넣기 (system("leaks a.out") 실행하는 함수)

3) 옵션

  • MallocStackLogging
    • 다른 터미널 창에 export MallocStackLogging=1 입력
    • 메모리 누수가 발생한 지점 확인 가능
  • list
    • system("leaks --list -- a.out")
    • 간결하게 정보 출력

📕 Makefile

1) 내장 매크로의 활용

  • make -p : 내부 매크로 확인
  • make -r : 내부 매크로 제거
# 매크로 정의 
RM = rm -f  # default. 생략 가능
CC = cc     # default. 생략 가능
AR = ar     # default. 생략 가능

CFLAGS = -Wall -Wextra -Werror
ARFLAGS = rsc

SRCS = main.c read.c write.c
OBJS = $(SRCS:.c=.o)   # 치환 참조

NAME = test.a

# 타겟 정의
all: $(NAME)

$(NAME): $(OBJS)
	$(AR) $(ARFLAGS) $@ $^

clean:
	$(RM) $(OBJS)

fclean: clean
	$(RM) $(NAME)

re: fclean
	$(MAKE) all

.PHONY: all clean fclean re

2) 오브젝트 생성 %.o : %.c

  • CFLAGS : C컴파일시 사용된 플래그 (ex. -Wall -Wextra -Werror)
# 기본 값
CC = cc
CFLAGS = -Wall -Wextra -Werror
CPPFLAGS = -I.
TARGET_ARCH =
OUTPUT_OPTION = -o $@
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

%.o: %.c
	$(COMPILE.c) $(OUTPUT_OPTION) $<
.c.o:
	$(COMPILE.c) $(OUTPUT_OPTION) $<

3) 정적 라이브러리 생성 (%) : %

  • ARFLAGS : ar 실행 시 사용할 플래그 (ex. rcs)
# 기본 값
AR = ar
ARFLAGS = rv

%.a:
(%) : %
	$(AR) $(ARFLAGS) $@ $<

4) 바이너리 생성 % : %.o

  • LDFLAGS : 링커 ld를 호출할 때 사용될 플래그 (ex. -L./lift)
  • LDLIBS : 링커 ld를 호출할 때 사용될 플래그 또는 이름 (ex. -lft)
  • 라이브러리를 링킹할 때 순서 중요
    • main.o > libftprintf.a > libft.a 순서대로 의존
    • cc main.o -libftprintf libft
CC = cc
LDFLAGS = -L.
TARGET_ARCH =
LDLIBS = -lft
LINK.o = $(CC) $(LDFLAGS)  $(TARGET_ARCH)

%: %.o
	$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
.o:
	$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

5) 외부 Makefile 활용

  • make -f : 한번의 make를 통해 2개 이상의 결과물 생성
all: server client

server:
	$(MAKE) -f Makefile.server

client:
	$(MAKE) -f Makefile.client
  • make -C : 하위 디렉토리의 Makefile 수행시 사용
SERVER = server
CLIENT = client

all bonus clean fclean re:
	$(MAKE) -C lib $@
	$(MAKE) -C src $@

$(SERVER) $(CLIENT):
	$(MAKE) -C lib
	$(MAKE) -C src $@

.PHONY: all clean fclean re bonus
  • 하위 라이브러리 빌드
.DEFAULT_GOAL = all

CFLAGS = -Wall -Wextra -Werror -MMD -MP
CPPFLAGS = -I./include
LDFLAGS = -L./lib
LDLIBS = -lftprintf -lft

LIBFT = libft/libft.a
$(LIBFT):
	$(MAKE) -C $(@D)
##################################################################
PUSHSWAP = push_swap

PUSHSWAP_SRCS = common.c push_swap.c
PUSHSWAP_OBJS = $(PUSHSWAP_SRCS:.c=.o)
PUSHSWAP_DEPS = $(PUSHSWAP_SRCS:.c=.d)
-include $(PUSHSWAP_DEPS)

$(PUSHSWAP): $(PUSHSWAP_OBJS)
$(PUSHSWAP_OBJS): $(LIBFT)
##################################################################
CHECKER = checker

CHECKER_SRCS = common.c checker.c
CHECKER_OBJS = $(CHECKER_SRCS:.c=.o)
CHECKER_DEPS = $(CHECKER_SRCS:.c=.d)
-include $(CHECKER_DEPS)

$(CHECKER): $(CHECKER_OBJS)
$(CHECKER_OBJS): $(LIBFT)
##################################################################
all:
	$(MAKE) -C $(dir $(LIBFT))
	$(MAKE) $(PUSHSWAP)

bonus:
	$(MAKE) -C $(dir $(LIBFT))
	$(MAKE) $(CHECKER)

clean:
	$(MAKE) -C $(dir $(LIBFT)) clean
	$(RM) $(wildcard *.o) $(wildcard *d)

fclean: clean
	$(RM) $(LIBFT) $(PUSHSWAP) $(CHECKER)

re: fclean
	$(MAKE) all

.PHONY: all clean fclean re bonus

6) 상위 디렉토리에서 하위 디렉토리 Makefile 실행

profile
Notion으로 이동 (https://24tngus.notion.site/3a6883f0f47041fe8045ef330a147da3?v=973a0b5ec78a4462bac8010e3b4cd5c0&pvs=4)

0개의 댓글