[Linux] 리눅스 개발환경 도구 ②

예빈·2025년 12월 18일

Embedded/Linux

목록 보기
11/21


0️⃣ 들어가며

리눅스 개발환경 도구의 두 번째이자 마지막 편이다.
make와 Makefile이 중요해서 거의 이 내용 위주로 작성되어 있다.


1️⃣ 학습 내용

make와 Makefile

✅ 컴파일 언어와 인터프리터 언어

  • 컴파일 언어

    소스를 한 번에 실행 파일로 변환 후 실행

    예 : C, C++

  • 인터프리터 언어

    실행 시에 한 줄씩 읽어가며 바로 실행

    내부적으로 on-the-fly 방식으로 컴파일이 일어남

    예 : Python, JavaScript, Bash

✅ make와 Makefile의 역할

  • make의 역할

    Makefile을 읽고 필요한 컴파일/링크 명령을 실행하는 빌드 자동화 도구

    여러 .c , 라이브러리가 있는 프로젝트를 빌드할 때 변경된 파일만 다시 컴파일해서 시간을 줄임

    같은 명령을 매번 입력하지 않아도 make 명령어 한 번으로 반복되는 작업을 처리

    임베디드에서는 C/C++을 주로 사용하기 때문에 빌드(컴파일) 작업 자동화가 중요함

  • Makefile의 역할

    타겟 파일, 의존성, 명령 등 빌드 규칙을 적어 둔 스크립트/설정 파일

  • make와 Makefile 사용의 이점

    빌드 명령이 길고 파일이 많다면 수동 컴파일은 비효율적이며 실수를 유발함

    협업 또는 배포 환경에서 Makefile을 명시해 두면 누구나 동일한 빌드 결과를 얻을 수 있음

  • make의 설치

    • Windows Visual Studio에서는 nmake 제공 MinGW, Dev-C++ 등에서 mingw32-make 제공
    • Ubuntu/Linux sudo apt install make 로 설치
  • 도움말 사용

    터미널에 make --help 또는 make -h 를 입력하여 옵션 및 사용법 요약 확인

✅ make 사용하기

  • Makefile 작성 기본 규칙

    target: prerequisites(dependencies)
    <TAB>recipe...

    bash가 실행하는 명령을 make가 대신 호출하는 형태

    target : 만들어질 대상 (실행파일, 오브젝트, 가상 타깃 등)

    prerequisites(dependencies) : 의존성, 타깃을 만들기 전에 먼저 있어야 할 파일

    recipe: 실제로 실행할 쉘 명령들로, 반드시 앞에 탭 문자(TAB)가 와야 함

  • 출력 제어와 @

    기본적으로 make는 쉘 명령을 echo 한 다음 실행됨

    recipe 앞에 @ 를 붙이면 터미널 출력을 막을 수 있음

    foo:
    	@echo foo
  • 타겟 실행과 디폴트 타겟

    make        # 아무 인자 없으면 첫 번째 타겟이 디폴트로 실행
    make foo    # foo 타겟만 실행
    make foo bar  # 여러 타겟을 한 번에 실행 가능

    Makefile의 가장 위에 있는 첫 번째 타겟은 디폴트 타겟

    관례적으로 all 이라는 이름을 디폴트 타겟으로 두고, 빌드해야 할 내용들을 의존성으로 작성

  • 올 타겟(all target)

    all: app1 app2 lib

    디폴트 타겟으로 주로 사용됨

    recipe가 없는 가상 타겟으로, 의존성에 있는 파일들을 모두 최신 상태로 만드는 역할

  • 의존성과 실행 순서

    foo:
            @echo foo
    
    bar: foo
            @echo bar
    
    baz: bar
            @echo baz
    
    ; make baz를 입력하면
    ; foo -> bar -> baz 순으로 실행

    타겟을 호출하면 해당 타겟의 의존성들이 먼저 빌드되고 나서 해당 타겟의 recipe 실행

✅ 사용할 Makefile 지정

  • 기본 Makefile 종류

    옵션 없이 make 를 실행하면, 아래 Makefile을 읽어들임

    세 개가 모두 있으면 순서대로 Makefile을 찾아 사용함

    1. GNUmakefile
    2. makefile
    3. Makefile
  • 옵션 지정하여 Makefile 사용하기

    기본 이름이 아닌 Makefile을 사용하려면 -f 또는 --makefile 옵션을 씀

    $ make -f MyRules.mk
    $ make --makefile=build.mk

✅ make 데이터베이스 확인

  • make의 데이터베이스

    make 는 내부에 데이터베이스를 가지고 있으며, make -p 로 내용을 볼 수 있음

    • Variables

      내장 변수, 환경 변수, Makefile에서 정의한 변수의 최종 값

    • Pattern-specific variables/rules

      특정 패턴에만 적용되는 변수/규칙

    • Files

      각 타겟과 그 의존성, 갱신 여부(타임스탬프 정보)

    • Implicit rules

      컴파일/링크에 대해 make 가 기본으로 사용하는 암시적 규칙 목록

  • 예시

    $ make -p | grep -w ^CC
    CC = cc          ; C 컴파일러는 cc
    
    $ make -p | grep -w ^LD
    LD = ld          ; 링커는 ld

✅ make 디버그하기

  • make 디버그 옵션

    make -d 를 입력하면 make 의 동작을 상세히 보여주어 처리 과정을 분석할 수 있음

    1. GNU Make 버전, 빌드 타깃, 라이선스(GPLv3) 같은 헤더 정보 출력
    2. 타겟에 대해 수많은 암시적 규칙을 적용(예: %.o : %.c%.c : %.y)
    3. 결과를 어떻게 판단했는지 로그로 출력
  • 예시

    all: bar baz
            @echo makefile
    
    bar:
            @echo bar
    
    baz:
            @echo baz
    $ touch all bar baz
    $ ls
    Makefile  all  bar  baz
    $ make
    make: 'all' is up to date.

    make는 기본적으로 파일 타겟을 다루는 구조

    폴더에 타겟 이름과 같은 실제 파일이 있고 타임스탬프가 더 최신이라면 작업이 일어나지 않음

    $ make -d
    Considering target file 'all'.
    File 'all' was considered already.
    No need to remake target 'all'.

✅ Makefile 작성 방법

  • Makefile의 구성
    • rule 구조

      target: prerequisites
      <TAB>recipe...

      target : 만들고자 하는 것 (파일, 실행 파일, 혹은 가상 타겟)

      prerequisites = dependencies : 타깃을 만들기 전에 먼저 준비되어 있어야 할 것들

      recipe : 실제로 실행할 쉘 명령 (반드시 TAB으로 시작)

    • 주석(comment)

      # comment

      # 뒤에 작성한 내용은 make 가 무시함

    • 변수(Macro / Variable), 줄 나누기

      VAR = world
      
      hello: ${VAR}
      	@echo hello \
      ${VAR}
      
      # world 파일에 의존하므로, 해당 파일을 만든 뒤에 정상 실행됨

      VAR = world : 변수 정의

      ${VAR} 또는 $(VAR)로 참조할 수 있고 셸 변수($VAR)와 헷갈리지 않게 ${VAR}를 많이 사용

      \ (Split-line) 로 길이가 긴 한 줄을 여러 줄로 나눌 수 있음

✅ Makefile 구조화

  • include로 다른 Makefile 불러오기

    하나의 Makefile 안에서 다른 설정 파일 등을 불러와 합쳐서 사용할 수 있음

    설정/변수 정의/공통 규칙 등을 별도의 파일로 관리하고 include로 합칠 수 있음

    (예 : include config.mk rules.mk paths.mk)

    # config.mk
    
    CC:=gcc
    
    # makefile
    include config.mk
    
    all:
            @echo my c compiler is ${CC}
  • 자식 Makefile 불러오기

    상위 Makefile에서는 하위 Makefile을 호출할 수 있음

    프로젝트를 디렉토리별로 나누어 관리 가능

    프로젝트/
    ├── Makefile          # 부모
    └── subdir/
        ├── Makefile      # 자식
        └── main.c
    # 부모 Makefile (루트 디렉토리)
    # echo 실행한 뒤에 subdir로 이동해서 그 안의 Makefile 실
    hello:
        @echo hello
        ${MAKE} -C subdir --no-print-directory
    • ${MAKE}

      ${MAKE}는 현재 사용 중인 make 프로그램을 의미하는 특수 변수이다.

      (예: 사용자가 make로 호출했으면 make , gmake로 호출했으면 gmake )

    • 디렉토리 변경 방법

      1. -C 옵션

        ${MAKE} -C subdir --no-print-directory
        # --no-print-directory 옵션을 주면 디렉토리 변경 메시지가 사라져 로그가 깔끔해짐

        명령 실행 전에 작업 디렉토리를 subdir 로 바꾸고 make 를 실행하라는 의미

      2. cd 명령어

        cd subdir && ${MAKE}

        같은 의미이지만 -C 로 표기하는 것이 선호됨

✅ 변수 상속

  • make 변수와 셸 변수

    Makefile의 변수(CCCFLAGS 등)는 make 내부에서만 보이는 변수

    일반 셸에서 echo $CC 같은 식으로는 확인 불가

    쉘 환경 변수는 make 실행 시에 기본값으로 들어오지만 Makefile에서 바꾼 것이 셸에 반영되지 않음

  • 자식 Makefile에 변수 전달

    부모 Makefile에서 자식으로 변수를 넘기려면 export를 사용

    # 부모 Makefile
    export CC = gcc
    export CFLAGS = -Wall -g
    
    hello:
    	@echo "CC in parent: $(CC)"
    	${MAKE} -C subdir
    
    # subdir/Makefile
    all:
    	$(CC) $(CFLAGS) -o main main.c    # 부모의 변수 사용

    export CC = gcc : make가 자기 자식 프로세스를 실행할 때 환경 변수로 CC를 넘겨줌

    ${MAKE} -C subdir로 자식 make를 실행하면, 자식 Makefile에서도 $(CC)$(CFLAGS)를 사용 가능

  • 전체 변수 자동 상속

    .EXPORT_ALL_VARIABLES 을 사용하면 모든 변수를 자식에게 자동으로 export 할 수 있음

    # 모든 make 변수 자동 상속
    .EXPORT_ALL_VARIABLES:
    
    CC = gcc
    CFLAGS = -O2
    hello:
        ${MAKE} -C subdir
  • 특정 변수의 상속 막기

    unexport 를 사용하면 특정 변수는 자식에게 넘겨주지 않음

    export CC = gcc
    unexport PRIVATE_VAR    # 자식에게 전달 안 함
    
    hello:
        ${MAKE} -C subdir

✅ 타겟

  • 디폴트 타겟

    기본으로 실행할 타겟을 디폴트 타겟으로 설정할 수 있음

    make 명령만 입력했을 때 Makefile의 최상단에 있고 . 으로 시작하지 않는 타겟이 디폴트 타겟이 됨

    관례적으로 이 타겟을 all 로 두고, 프로젝트 전체 빌드를 걸어 둠

    디폴트 타겟을 명시적으로 바꾸기 위해서는 .DEFAULT_GOAL:=타겟명 으로 지정

  • 파일 타겟과 빌드 여부 판단

    1. 타겟 파일 존재 여부 확인
               ↓
    2. 존재 시 → 타겟 타임스탬프 vs 의존성 파일 최신 타임스탬프 비교
               ↓ 타겟이 더 최신
    3. 타겟 최신 → "make: 'target' is up to date." (스킵)
               ↓ 타겟이 더 오래되거나 없음
    4. 명령 실행 → 타겟 재생성 (타임스탬프 갱신)
    타겟실제 파일 존재 여부의존성과의 관계동작
    app없음-컴파일/링크해서 생성
    app있음타겟이 최신make: 'app' is up to date.
    app있음의존성이 더 최신다시 컴파일/링크
    clean없음 (보통 파일 아님)-항상 실행되게 .PHONY로 선언

    타임스탬프에는 access(읽음), modify(내용 수정), change(링크 등 메타데이터 변경)가 있음

    make 가 사용하는 것은 modify(mtime, 내용 변경 시각)임

  • Trivial Prerequisite

    타겟을 재생성할 필요가 없는 특별한 의존성 상태

    의존성이 있더라도 내용을 건드릴 필요 없이 타임스탬프만 맞추면 되는 상황

  • make -t 옵션

    각 타겟을 실제로 빌드하지 않고 touch 만 해서 타임스탬프를 갱신

    mtime만 최신으로 표시하는 용도이며, 실제로 명령은 실행하지 않음

✅ 빌드를 위한 rule 작성

  • 파일 타겟

    실제 파일 이름을 가진 타겟으로, 타임스탬프 비교를 통해 재성성 여부를 판단

  • 소스 코드

    입력 파일로, 변경되면 의존하는 목적 코드나 실행 파일을 갱신해야 함

  • 목적 코드

    소스 파일에서 컴파일된 중간 결과물, 링커가 실행 파일을 만들 때 사용

  • 실행 코드(타겟)

    최종적으로 만들어지는 실행 파일, 보통 가장 먼저 만드는 디폴트 타겟에 지정

    all: hello           # 디폴트 타겟
    
    hello: hello.o       # 실행 파일 타겟 
            cc -o hello hello.o
    
    hello.o: hello.c     # 목적 코드 타겟
            cc -c hello.c

✅ 여러가지 타겟

  • 더미 타겟(Dummy Target)

    실제로 파일을 생성하지 않지만, 빌드 작업을 제어하거나 편의성을 위해 사용하는 가상의 타겟

    타임스탬프와 관련 없이 실행하면 계속 실행할 수 있음

    • 디폴트 타겟 ( all )

      실제 파일을 만들지 않고 여러 타겟을 묶는 제어용 타겟

    • 가상 작업 타겟 (clean, install)

      파일을 만들지 않고 작업 이름만 가진 타겟

    • 강제 재빌드 (FORCE)

      항상 Non-trivial prerequisite 상태로 만들어 재빌드 강제

  • 포니 타겟(.PHONY)

    실제 파일이 아닌 가상의 타겟으로, 항상 명령을 실행하게 하는 특별한 선언

    .PHONY: 타겟명 으로 나열한 타겟들은 파일 존재 여부와 상관없이 항상 실행

    따로 선언하지 않은 더미 타겟은 파일이 있으면 스킵되므로 .PHONY로 선언해야 정상 동작

  • 표준 정리 타겟

    타겟삭제 내용목적.PHONY
    all삭제하지 않음전체 빌드(디폴트)
    clean.o, 실행파일개발 중 임시파일 정리
    distcleanclean+ config 파일소스 배포 상태로 복원
    clobberdistclean+ 모든 생성물완전 초기화
  • 포스 타겟 (FORCE)

    항상 재빌드 필요(Non-trivial prerequisite) 상태로 만드는 독립적인 더미 타겟

    캐시 무효화나 강제 갱신 목적으로 사용

    # 매번 데이터 재생성
    data.txt: generate.sh FORCE
    	./generate.sh > data.txt
    
    FORCE:
    	# 의도적으로 빈 recipe
  • 이차 타겟 (Secondary Target)

    중간 파일을 make가 자동으로 삭제하지 않도록 하는 선언

    # 모든 .o 파일 보존
    .SECONDARY: %.o
    
    # 모든 중간 파일 보존
    .SECONDARY:
  • 조용한 타겟 (Silent target)

    레시피 명령어 실행 전 echo를 억제하는 타겟으로, 출력을 깔끔하게 정리하는 기능

    .SILENT: 타겟명

✅ 접미사과 패턴 규칙

  • 단일 접미사 규칙(Single suffix rule)

    확장자가 없는 타겟을 특정 소스로부터 만들어 주는 옛날 형식의 implicit rule

    # makefile
    
    foo: foo.c
    # 빈 recipe로 두어도 내장 규칙으로 자동 컴파일
    • 접미사 목록

      make -p | grep .SUFFIXES 결과를 확인하면 make 기본 suffix 목록을 볼 수 있음

      .SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo .w .ch .web .sh .elc .el

      makefile 내에 .SUFFIXES: 를 작성하면 지정한 목록으로 접미사 리스트를 덮어씀

      내장된 suffix를 사용할 수 없고, explicit rule/다시 정의한 규칙만 사용됨

  • 이중 접미사 규칙(Double suffix rule)

    소스 접미사에서 타겟 접미사를 만드는 변환 규칙을 정의하는 옛날 형식의 implicit rule

    .c.o:
    	cc -c $< -o $@
  • recipe-less suffix rule

    .SUFFIXES 뒤에 레시피가 없는 더미 규칙

    GNU의 make는 내장된 규칙을 사용하기 때문에 실질적으로는 의미 없음

  • 패턴 룰(Pattern Rule)

    % 와일드카드를 사용해 여러 파일에 동일한 변환 규칙을 한 번에 적용하는 암시적 규칙

    현대 Makefile에서 권장되는 형태

    타겟: 의존성
    	레시피
    
    # 예시
    %.o: %.c
    	cc -c $< -o $@
    • 타겟에 적용하는 규칙 우선순위
      1. 명시적 규칙 (Explicit Rule)
        │ foo: foo.c bar.c
        └─ 정확히 일치하는 타겟
      2. 사용자 정의 패턴 규칙 (Pattern Rule)
        │ %.o: %.c
        └─ foo.o: foo.c (패턴 매칭)
      3. 사용자 정의 접미사 규칙 (Suffix Rule)
        │ .c.o:
        └─ .SUFFIXES 순서대로
      4. 내장 접미사 규칙 (Built-in Suffix)
        └─ .c.o: (GNU make 기본)
      5. 내장 패턴 규칙 (Built-in Pattern)
        └─ %.o: %.c (기본)
      6. Match-anything 규칙 (%:)

✅ recipe 심화

  • recipe 추가하기

    recipe는 타겟에 대해 실제로 실행되는 쉘 명령의 집합

    모든 recipe 줄은 반드시 탭 문자로 시작(tab-leading)해야 함

  • recipe 함수 만들기

    여러 타겟에서 같은 recipe를 반복하고 싶을 때 define 으로 내용을 정의할 수 있음

    defineendef 사이에 여러 줄의 recipe를 작성

    define BUILD_APP
    	@echo building $@
    	@cc -c main.c
    	@cc -o $@ main.o
    endef
    
    app1:
    	$(BUILD_APP)
    
    app2:
    	$(BUILD_APP)
  • recipe에서 for 루프 만들기

    recipe는 기본적으로 한 줄로 작성하기 때문에 for 루프를 쓰려면 \$$ 에 주의하여 사용

    \ : 줄을 잇는 역할로, 쉘 입장에서는 한 줄짜리 for문을 받을 수 있도록 함

    $$ : make 처리 시에 $ 하나가 사라지기 때문에 쉘에 $x 로 전달하기 위해 두 번 사용

    LIST = a b c
    
    loop:
    	@for x in $(LIST); do \
    		echo $$x; \
    	done

✅ Makefile의 조건 실행

  • Makefile의 조건문

    조건문을 사용하여 특정 부분을 포함하거나 제외할 수 있음

    조건은 Makefile을 읽는 시점에 평가됨

  • 값을 비교하는 조건

    ifeq , else , endif

    같은 값인지 판단하여 블록을 활성화/비활성화하는 조건문

    # 3가지 형태 지원
    ifeq ($(VAR), value)
    ifeq "$(VAR)" "value"
    ifeq '$(VAR)' 'value'
    ifeq ($(VAR), value)
        # VAR == value 일 때만 이 블록이 유효
        MSG := "matched by , 형식"
    else
        MSG := "not matched"
    endif
  • 정의 여부를 확인하는 조건

    ifdef, ifndef

    변수가 정의되어 있는지 여부를 확인하는 조건문으로, 값은 검사하지 않음

    # CC가 환경변수나 Makefile에 정의되어 있는지 체크
    ifdef CC
    	COMPILER = $(CC)
    else
    	COMPILER = gcc
    endif
    # 정의되어 있지 않을 때를 체크하려면 ifndef 사용

✅ 변수

  • recipe 밖의 변수 정의

    Makefile 언어의 변수로, 이전에는 macro라고 호칭하던 것

    변수는 전역으로 사용되며 이름은 대문자로 설정하는 것이 권장됨

    • 대표적인 대입 연산자

      V  = foo     # 지연(재귀) 확장(Deferred / Recursive)
      V1 := foo    # 즉시(단순) 확장(Immediate / Simple)
      V2 ?= foo    # 약한 대입(Weak), 값이 비어 있을 때만 설정
      V3 += bar    # 기존 값에 이어 붙이기
    1. Deferred (=)

      V = foo

      참조되는 시점에 오른쪽 값을 다시 해석

      다른 변수들과 혼용하여 쓸 때 값이 바뀌었다면 변경된 값으로 적용

    2. Immediate (:=)

      V := foo

      정의 시점에 오른쪽을 한 번 평가하고 그 결과만 저장

      이후 다른 변수 값이 바뀌어도 영향 없음

    3. Weak (?=)

      변수 값이 이미 정의되어 있으면 건드리지 않고, 비어 있을 때만 대입

    4. Append (+=)

      기존 값 뒤에 공백을 두고 문자열 추가

  • recipe 안의 변수 정의

    $(eval) 은 문자열을 Makefile 코드처럼 파싱하여 실행 시점에 동적으로 변수를 생성하는 함수

    make의 전역 변수 생성은 되지만 다른 recipe에서 사용 불가

  • 변수 해제하기

    undefine 변수명 으로 이미 정의된 변수를 없앨 수 있음

  • 변수 참조 방법

    문법예시
    $변수명$CC
    $(변수명)$(CC)
    ${변수명}${CC}

    ${변수명} 형태가 쉘 변수와 혼동이 없어 가장 권장됨

  • 변수 치환

    변수 문자열에서 특정한 패턴을 찾아 치환하는 간편한 문법

    $(변수명:패턴=치환문자열)

    패턴 : %.x (와일드카드 % 포함)

    치환 : .y (패턴의 % 부분 유지)

    SOURCES = main.c utils.c parse.c
    OBJECTS = $(SOURCES:.c=.o)
    # 결과 : OBJECTS = main.o utils.o parse.o
    
    CFLAGS  = $(CFLAGS:-g=-O2)    # -g → -O2
  • Computed Variable Names

    foo = bar
    bar = baz
    
    VAR = $($(foo))    # → $(bar) → baz

    변수 이름 안에 변수를 참조하는 형태

  • 자동 변수(Automatic Variables)

    현재 타겟과 의존성에 대해 자동으로 설정하는 특수 변수로 자주 사용됨

    변수의미예시 (foo.o: foo.c bar.h)
    $@타겟 전체foo.o
    $<첫 번째 의존성foo.c
    $^모든 의존성foo.c bar.h
    $+중복을 포함한 모든 의존성foo.c bar.h bar.h (순서대로)
    $?타겟보다 나중에 변경된 의존성foo.c (bar.h 최신이면)
    $*stem (패턴 규칙)foo (%.o: %.c에서)
    %.o: %.c
    	gcc -c $< -o $@       # foo.c → foo.o
    
    lib%.a: %.o
    	ar rcs $@ $^          # libfoo.a: foo.o (lib%.a 패턴)
    • 자동 변수 확장형(참고)

      이전에 타겟 경로와 파일명을 분리하는 데 사용되었으며 현재는 dir/notdir 함수를 주로 사용

      변수의미예시 (obj/foo.o: src/foo.c)
      $(@D)타겟 디렉토리obj
      $(@F)타겟 파일명foo.o
      $(<D)첫 의존성 디렉토리src
      $(<F)첫 의존성 파일명foo.c

CMake

✅ CMake

  • CMake

    빌드 스크립트를 만들어 주는 메타 빌드 시스템

    CMakeLists.txt라는 빌드 설정 파일을 기준으로, 타겟을 정의하고 컴파일 옵션·링크할 라이브러리 등의 속성을 설정

    Makefile/Ninja/VS 프로젝트 등 각 환경에 맞는 빌드 시스템 파일을 자동 생성

  • CMake의 설치와 사용

    # 설치
    sudo apt install cmake
    
    # 사용법
    cmake .                          # 현재 디렉토리에서 빌드 파일 생성
    cmake build/                     # 별도 build 디렉토리에서 재생성
    cmake -S . -B build              # 명시적 source/build 지정
    cmake -S . -B build -G "BS_name" # 빌드 시스템 지정

    S <source> : 소스 디렉토리 설정

    B <build> : 빌드 결과물 디렉토리 지정

    G <generator> : 생성할 빌드 시스템 (Unix Makefiles, Ninja, Xcode 등)

  • CMakeLists.txt

    CMake 빌드 시스템의 핵심 입력 파일로

    프로젝트의 소스 파일, 타겟(실행 파일이나 라이브러리), 컴파일 옵션, 의존성 등을 정의하는 스크립트

    cmake_minimum_required(VERSION 3.10)
    project(MyProject)
    
    # 실행 파일 타겟 정의
    add_executable(hello main.c util.c)
    
    # 컴파일 옵션
    target_compile_options(hello PRIVATE -Wall -g)
    
    # 라이브러리 링크
    target_link_libraries(hello PRIVATE m pthread)

    project(NAME) : 프로젝트 이름 정의

    add_executable(NAME SOURCES...) : 실행 파일 타겟

    add_library(NAME SOURCES...) : 라이브러리 타겟

    target_*() : 타겟별 속성 설정 (옵션, include, 링크 등)

✅ CMake 사용하기

  • CMakeLists.txt 기본 템플릿

    # 주석은 #으로 시작
    
    cmake_minimum_required(VERSION 3.16)  # 최소 필요 버전 명시
    project(MyApp)                        # 프로젝트 이름 지정
    
    message("hello cmake!")               # 출력 메시지
    
    # 실행 파일 타겟 추가
    add_executable(${CMAKE_PROJECT_NAME} 
        hello.c 
        world.c
    )

    cmake_minimum_required() : 최소 필요 버전을 필수적으로 명시하여 호환성을 보장

    project(NAME) : 프로젝트명을 정의하고 이후 ${CMAKE_PROJECT_NAME}으로 참조

    message() : 빌드 로그 출력

  • CMake로 빌드하기

    # 1. 버전 확인
    cmake --version   # cmake version 3.16.3
    
    # 2. 빌드 디렉토리 생성 + Makefile 생성
    cmake -S . -B build
    
    # 3. 프로젝트 빌드
    cmake --build build          # 권장
    cd build && make -j4         # 전통적인 방법
    
    # 4. 실행
    ./build/MyApp
  • 라이브러리 만들고 링크하기

    # 정적 라이브러리
    add_library(mylib STATIC mylib.c)
    
    # 공유 라이브러리  
    add_library(mylib_shared SHARED mylib.c)
    
    # STATIC 명시하지 않아도 기본은 정적
    add_library(mylib mylib.c)
    add_executable(MyApp main.c)
    
    # 라이브러리 링크
    target_link_libraries(MyApp PUBLIC mylib)
    # 또는 PRIVATE mylib (내부용), INTERFACE mylib (헤더만)
    
    # 서브 디렉토리 추가, 하위 CMakeLists.txt 실행
    add_subdirectory(dirname)

Git과 GitHub

✅ Git과 GitHub

  • Git

    분산 버전 관리 시스템으로, 소스 코드 변경 사항을 관리하고 협업을 편리하게 하는 도구

    구분주요 내용
    버전 관리파일 변경 이력을 시간순으로 저장

    이전 상태로 되돌리기
    변경 내용 추적 |
    | 백업 | 로컬 저장소 + 원격 저장소
    push만 해도 백업 효과
    복구 용이 |
    | 협업 | 브랜치 기반 병렬 작업
    pull/push로 변경 공유
    PR로 코드 리뷰 가능 |

  • Git/Github 비교

    항목GitGitHub
    역할분산 버전 관리 시스템, 로컬 저장소원격 저장소 호스팅 서비스
    기능커밋, 브랜치, 머지Git + PR, Issue, Wiki, CI/CD
  • Git 설치와 초기 설정

    # 설치
    $ sudo apt install git
    $ git --version
    
    # 사용자 정보 설정
    $ git config --global user.email "your@email.com"
    $ git config --global user.name "Name"
    
    # 확인
    $ git config --global --list
  • Git 워크플로우

    1. 작업 트리 (Working Directory)

      수정/생성한 파일들

    2. 스테이징 영역 (Index/Staging Area)

      git add로 올린 파일들

    3. 로컬 저장소 (Repository)

      git commit으로 저장된 히스토리
      # 1. 파일 수정/생성
      $ echo "hello" > file.txt
      
      # 2. 스테이징 (add)
      $ git add file.txt          # 특정 파일만 스테이징
      $ git add .                 # 모든 변경사항을 스테이징
      $ git add *.c               # 패턴에 해당하는 파일 스테이징
      
      # 3. 커밋 (commit)
      $ git commit -m "Add file.txt"  # 커밋 메시지 남기기
  • Git 주요 명령어

    명령기능
    git init새 저장소 초기화
    git clone <url>원격 저장소 복사
    git status상태 확인 (가장 자주 씀)
    git add <file>스테이징
    git commit -m "msg"커밋
    git log커밋 히스토리
    git checkout <commit>특정 커밋으로 이동
    git reset --hard HEAD~1최근 커밋 취소
    git remote add origin <url>원격 저장소 추가
    git push origin main푸시
    git pull origin main

Docker

✅ Docker

  • Docker

    컨테이너를 생성·관리·배포하는 오픈소스 플랫폼

    애플리케이션을 깔아서 쓸 수 있는 환경을 똑같이 만드는 것

    애플리케이션을 인프라와 분리하여 빠르게 배포 가능

  • 가상화 (Virtual Machine, VM)

    CPU, 메모리, 디스크, 네트워크 등 하드웨어 전체를 가상 환경으로 만듦

    각 VM마다 완전한 OS가 필요하므로 차지하는 용량이 큰 편

    VM 간에는 독립되어 있으므로 보안이 강력함

  • 컨테이너 (Docker)

    패키지란 애플리케이션 실행에 필요한 코드, 라이브러리, 설정 파일 등을 하나로 묶은 것

    이를 격리된 컨테이너에서 실행하여 모든 환경에서 동일하게 동작하도록 하는 기술

    호스트의 OS 커널 하나를 공유하면서 다른 어플리케이션과 격리된 환경에서 실행

    하이퍼바이저→가상머신 여러 개 운영보다는 가벼운 레벨

  • Docker 워크플로우

    1. docker pull  # Docker Hub에서 이미지 가져오기
       docker pull ubuntu:20.04
    
    2. docker build # Dockerfile로 이미지 빌드
       docker build -t myapp .
    
    3. docker run   # 컨테이너 실행
       docker run -d -p 8080:80 myapp

2️⃣ 느낀 점

make를 안 써본 건 아니지만 잘 모른다고 생각했는데,
내가 생각했던 것보다 더 몰랐던 것 같다.
빌드 과정도 잘 모르고 gcc를 썼었는데, 그 부분에 대한 개념도 잡혔고
make랑 타겟에 대해서도 쭉 배우면서 작성된 스크립트를 이해할 수 있게 되었다.
나중에 프로젝트에서 잘 활용해 보면 좋겠다.

0개의 댓글