레이블이 튜토리얼인 게시물을 표시합니다. 모든 게시물 표시
레이블이 튜토리얼인 게시물을 표시합니다. 모든 게시물 표시

우분투에서 C++ 개발하기(3) - CMake

앞선 시리즈 :
우분투에서 C++ 개발하기(1) - https://ladofa.blogspot.com/2018/07/c-1.html
우분투에서 C++ 개발하기(2) - https://ladofa.blogspot.com/2020/08/c-2.html
여기서 사용하는 예제는 두 번째 시리즈에서 사용한 예제를 그대로 사용한다. 예제를 몰라도 읽는데 문제는 없다.

-------------------------

1편과 2편을 잘 정독했던 분들이라도 여기서부터는 예제가 복잡해서 이해하기 쉽지 않다. 쉬운 예제로 설명하고 싶은데.. 불가능하다. cmake가 원래 복잡한 컴파일 과정을 간단하게 하는 거라서... 여기서부터는 구경만 해도 좋고, 어쩌다 CMakeLists.txt 를 분석할 일이 있으면 그 때 한 번 더 봐주시길...

-------------------------


리눅스에서 C++ 코드를 컴파일하려면 최종적으로는 gcc나 다른 컴파일러를 이용해서 컴파일 명령을 내려줘야 한다. 그러나 파일 개수가 많고 의존성이 방대한 큰 프로젝트에서 일일이 gcc 명령을 내릴 수는 없다. 이를 보완하기 위해 Makefile을 작성해서 컴파일 명령어를 미리 입력해두면 나중에는 별 다른 수고 없이 간단한 명령으로 컴파일이 가능하다. 그런데 Makefile을 작성할 때도 결국은 gcc를 잘 알고 있어야 하고, 각종 옵션과 명령어를 직접 입력하는 것도 힘든 일이다.

CMake 기본


CMake는 이런 작업들조차 단순화하여 간단한 명령어를 통해 입력 가능하도록 돕는다. CMake는 사용자가 작성한 스크립트를 해석해서 Makefile을 만든다. 결국 cmake 를 실행한 뒤 make를 다시 실행해야 한다.

CMake도 프로그램이기 때문에 안 깔려 있으면 직접 깔아야 하는데 우분투에서는 다음과 같이 패키지를 통한 설치를 지원한다.

$sudo apt install cmake


여기서 CMake의  apt 패키지 이름이 cmake이고, 실행 프로그램의 이름도 cmake이다. 터미널에 그냥 cmake라고 입력해보자.

Usage

  cmake [options] <path-to-source>
  cmake [options] <path-to-existing-build>
  cmake [options] -S <path-to-source> -B <path-to-build>

Specify a source directory to (re-)generate a build system for it in the
current working directory.  Specify an existing build directory to
re-generate its build system.

Run 'cmake --help' for more information.

위와 같이 뜨면 성공이다. 컴퓨터에 깔려 있는 cmake 프로그램을 실행한 것이다. 버전을 확인하고 싶으면 다음과 같이 입력한다.

$cmake --version

cmake가 인식하는 스크립트 파일의 이름은 CMakeLists.txt 로 정해져있다. make가 기존에 존재하고 있는 Makefile 을 자동으로 인식하는 것처럼 cmake 역시 CMakeLists.txt 라는 이름의 파일을 찾는다.

이전 시리즈에서 작업했던 것과 같이 my.cpp, my.h, main.cpp 파일이 있다고 가정할 때, 이를 빌드하기 위한 CMakeLists.txt 스크립트는 다음과 같다.


cmake_minimum_required (VERSION 3.10)
project (mytest)
add_executable (mytest my.cpp main.cpp)

맨 첫째 줄은 cmake의 최소 버전을 밝히는 것이다. 해당 버전보다 아래인 cmake로 실행할 수  없게끔 막는다.
project()는 해당 스크립트의 프로젝트 이름을 밝힌다. mytest는 이 프로젝트의 이름이며 실행파일을 생성할 경우 실행파일의 이름이 될 것이다.
add_executable은 이 스크립트를 통해 최종적으로 실행파일을 만들고자 한다는 뜻이다. 실행파일 말고 정적 라이브러리를 만들고 싶을 때는 add_library라는 명령을 이용한다.

이 스크립트를 실행하려면 다음과 같이 입력하면 되는데...

$cmake .

make 는 무조건 실행한 경로에서 Makefile의 존재유무를 찾지만 cmake는 반드시 CMakeLists.txt의 경로를 지정해줘야 한다. cmake 다음에 나오는 점(.)이 바로 경로를 나타내는 것이다. 점 하나 찍으면 현재 경로를 말한다.

cmake는 Makefile을 생성하면서 기타 다양한 파일들도 생성하므로 그냥 현재 폴더에서 실행하면 이런저런 파일이 생겨 지저분해진다. 그래서 보통은 build 디렉토리를 만들어서 거기서 실행한다.

$mkdir build
$cd build
build$cmake ..

위와 같이 build 디렉토리를 만든 뒤, 해당 디렉토리 내에서 cmake를 실행한다. 이 때 cmake 뒤에는 점 두개 (..) 가 붙어 있다. 이는 상위 디렉토리를 나타낸다. CMakeLists.txt 파일이 build디렉토리에 있지 않고 그 상위 디렉토리에 있기 때문이다.

빌드에 성공하면 이런 저런 메타파일이 생성되고, 가장 중요한 Makefile이 만들어진다. 이 Makefile의 타겟은 기본적으로 all 과 clean을 가지고 있다. 타깃을 입력하지 않으면 all을 기본으로 한다. 아래와 같이 입력해보자.

build$make

그냥 make라고 하면 자동 생성된 Makefile을 실행시킨다. 여기까지는 거의 공식처럼 사용한다.



빌드에 성공하면 mytest 파일이 만들어져 있다. mytest를 실행해보자.

build$./mytest
3^2 == 9

이제 main.cpp파일을 수정할 것이다. 다음 프린트 구문을 추가한다.

printf("This code is modified.\n" );

다시 mytest를 빌드해서 실행해보자.

build$make
build$./mytest
3^2 == 9
This code is modified.

위와 같이 수정되서 나타난다. 뭔가 신기한 일이 이뤄진 것 같지만 사실 별 것도 아니다. 여기서 일단 CMake는 하는 일이 없다. make를 실행하면 수정된 파일을 감지해서 다시 빌드를 해준다. 그리고 그 결과가 나타난 것 뿐이다.

이제 your.h, your.cpp 파일을 추가할 것이다.

<your.h>
int your_func(int x);

<your.cpp>
#include "your.h"

int your_func(int x)
{
    return x + 10;
}


main.cpp에서도 your_func 을 사용하도록 수정한다.

<main.cpp>
#include <iostream>
#include "my.h"
#include "your.h"

int main(void)
{
    printf("%d^2 == %d\n", 3, my_func(3));
    printf("This code is modified.\n" );
    printf("%d + 10 == %d\n", 3, your_func(3));
}


마지막으로 CMakeLists.txt파일을 다음과 같이 수정한다.

cmake_minimum_required (VERSION 3.10)
project (mytest)
add_executable (mytest my.cpp main.cpp your.cpp)


add_executable에 your.cpp 를 추가한 것이다. 이제 아까와 마찬가지로 새로 빌드를 해본다.

build$make
build$./mytest
3^2 == 9
This code is modified.
3 + 10 == 13

수정된 결과가 반영되어 있다. cmake는 수정된 CMakeLists.txt 파일을 자동으로 반영하여 Makefile을 미리 바꿔놓았다. 그리고 나는 make명령어를 통해  바뀐 Makefile을 실행해서 결과를 확인했다. 

만약 CMakeLists.txt에 오류가 있으면 어떻게 될까?

cmake_minimum_required (VERSION 3.10)
project (mytest)
add_executable (mytest my.cpp main.cpp your.cpp nobody.cpp)

build$make

나는 make를 실행했는데, 실행해보면 CMakeLists.txt파일이 잘못되었다는 에러 메시지가 나온다. cmake는 건들지도 않았는데!

make는 어떻게 cmake를 인식하고 일반적인 빌드 오류가 아닌 cmake 오류를 출력하는 것일까? make가 cmake와 뭔가 연동되어 있는 것일까? Makefile 내부를 보면 cmake 에러가 없는지 확인하고 메시지를 출력하도록 되어 있다. cmake가 Makefile을 생성하면서 그 속에 cmake관련 내용을 스파이처럼 넣어놨다. 그래서 make만 실행해도 CMakeLists.txt의 오류를 검사하게 된다.


라이브러리 구조화

이제 my 라이브러리를 분리해서 컴파일해보자. 보통 프로그램 개발에서 핵심적인 기능을 담당하는 모듈은 기본 프로그램과 분리해서 따로 라이브러리 형식으로 개발하고, 본 프로그램은 해당 라이브러리를 사용하도록 만든다. 지금 예제에서는 mylib 라는 이름의 라이브러리를 개발하는 것으로 가정할 것이다.

일반적으로 라이브러리를 포함한 구조는 다음과 같이 되어 있다. 이 구조를 무조건 지킬 필요는 없지만 보통 이렇다..

  - 라이브러리 이름
    - src
      - files.cpp
    - include
      - files.h
    CMakeLists.txt
  - 또 다른 라이브러리
    ......
main.cpp
CMakeLists.txt

이렇게 글씨로 쓰면 구조가 안 보일까봐 VSCode 캡쳐 화면도 준비했다.



라이브러리마다 CMakeLists.txt 파일이 따로 있고 여기에는 src 디렉토리와 include 디렉토리가 있다. 빌드를 마치고 나서 src 폴더에 있는 cpp파일은 당연히 소스코드니까 공개하지 않지만 include 폴더에 있는 h, hpp 파일은 라이브러리를 사용하는 측에게 공개해야 한다. C의 라이브러리들은 헤더파일이 있어야 쓸 수 있다. 그런 이유를 포함해서 관리상의 편리함 등 여러 가지 이유로 src와 include는 분리해놓는 것이다.

혹시나 다른 사람이 만든 C언어 소스를 보게 되면 항상 include 디렉토리와 src 디렉토리가 따로 나뉜 걸 보게 된다. 또한 src를 컴파일한 결과는 bin 혹은 build 디렉토리에 모셔놓게 된다. 만약 소스코드가 아닌 빌드된 라이브러리를 다운받게 되면 include 속에 있는 헤더파일과 bin디렉토리 내부의 컴파일된 바이너라 파일만 보게 된다. 왜 그런지를 이해하려면 1편을 참고한다...


하여튼 mylilb 속에 있는 CMakeLists.txt의 내용은 다음고 같다.


cmake_minimum_required (VERSION 2.8)
project (mylib)
add_library (mylib src/my.cpp)
target_include_directories(mylib PUBLIC include)


아까의 CMakeLists.txt와 비교해서 다른 점은 add_executable 대신 add_library로 바뀌어 있다는 것이다. 실행파일을 만들지 않고 라이브러리를 생성한다는 뜻이다. 여기에는 사용되는 모든 cpp파일을 적어주어야 한다. 그리고 target_include_directories가 추가되었는데, 여기에 include 디렉토리를 알려줘야 한다.

다음과 같이 빌드해볼 수 있다.

mylib$mkdir build
mylib$cd build
mylib/build$cmake ..
mylib/build$make

빌드하고 나면 libmylib.a 파일이 생성되었다. 이것을 메인에서 이용하면 된다. 바깥쪽에 있는 CMakeLists.txt의 내용은 다음과 같다.

cmake_minimum_required (VERSION 2.8)
project (mytest)
add_executable (mytest main.cpp)
target_link_libraries(mytest PUBLIC mylib)
target_link_directories(mytest PUBLIC mylib/build)
target_include_directories(mytest PUBLIC mylib/include)

target_link_libraries에 라이브러리 이름을 추가한다. 라이브러리 이름이란 *.a에서 맨 앞에 lib를 제외한 것이다. gcc빌드할 때와 마찬기자로 라이브러리 파일은 맨 앞에 lib가 prefix로 붙는다. 파일 확장자가 앞쪽에 붙었다고 생각하면 된다.

target_link_directories 에는 라이브러리 파일의 경로를 추가한다. target_include_directorie 에는 헤더 파일의 경로를 추가하면 된다.

사실 이런 식으로 하는 것은 아예 동떨어진 다른 라이브러리를 가져오는 것과 동일하다. 그런데 현재 디렉토리/프로젝트 구조는 mylib 를 포함하고 있는데, 이 특징을 전혀 살리지 않은 것이다. 하위 디렉토리에 라이브러리가 들어있는 상황에서는 조금 더 간결하게 cmake명령을 만들 수 있다.

이제 add_subdirectory 를 이용해서 디렉토리를 구조화한 보람을 찾아보자.  mylib 디렉토리 내에 있는 CMakeLists.txt는 그대로 두고 메인 프로그램의 CMakeLists.txt를 다음과 같이 수정한다.

cmake_minimum_required (VERSION 2.8)
project (mytest)
add_subdirectory(mylib)
add_executable (mytest main.cpp)
target_link_libraries(mytest PUBLIC mylib)

add_subdirectory에 mylib를 추가했다. 이렇게 하면 mylib 디렉토리 속에 있는 CMakeLists.txt를 인식하고 가져온다. 라이브파일의 경로나 헤더 파일의 경로는 따로 추가할 필요가 없다. 다만 target_link libraries에 mylib를 추가하면 끝이다.

이렇게 하고 build 디렉토리를 만들어서 cmake를 해보면 mylib와 mytest가 같이 만들어지게 된다. 즉 여러 개의 CMakeLists.txt에 각각 명령어를 실행할 필요 없이 한 번에 연쇄적으로 실행된다. 실제로 build 내부에 mylib 디렉토리가 따로 생성되며 여기에 빌드된 라이브러리가 들어 있다.



외부 패키지 추가


마지막으로 외부 패키지를 찾아보도록 하자. 다른 사람이 빌드한 라이브러리를 추가할 때 위에서 소개한 바와 같이 include 디렉토리와 라이브러리를 일일이 추가하는 것은 상당히 번거롭다. 때문에 누군가 고수님께서 우리를 위해 라이브러리를 만드실 때는 CMake를 활용해서 자기 라이브러리를 잘 추가할 수 있도록 메타 정보를 같이 만들어둔다. 우리는 해당 메타 정보만 읽어들이면 라이브러리 추가에 필요한 각종 파라미터(include디렉토리, lib 디렉토리 등)를 자동으로 얻을 수 있다.

대표적인 라이브러리로 OpenCV가 있다. OpenCV를 본 프로젝트에 추가하려면 CMakeLists.txt에 다음과 같이 입력한다.


cmake_minimum_required (VERSION 2.8)
project (mytest)
find_package(OpenCV REQUIRED)
add_subdirectory(mylib)
add_executable (mytest main.cpp)
target_link_libraries(mytest PUBLIC mylib ${OpenCV_LIBS})
target_include_directories(mylib PUBLIC ${OpenCV_INCLUDE_DIRS})


find_package는 주어진 이름의 패키지(메타정보)를 찾아서 라이브러리의 목록과 헤더 파일의 디렉토리 목록을 변수에 저장한다. OpenCV 패키지는 어떻게 찾는가? 그 방법에 대한 링크가 있다.


대략 9가지 정도의 방법으로 패키지를 찾는다(...) 보통은 라이브러리 설치할 때 sudo make install 과 같은 명령어를 입력하면 그 속에 CMake 패키지 등록 과정이 포함되어 있다.

라이브러리를 찾는데 성공하면 OpenCV_LIBS와 OpenCV_INCLUDE_DIRS 가 변수로 제공된다. 이것을 타겟 빌드하는데 추가해주면 된다. 다른 라이브러리인 경우 OpenCV 자리에 다른 이름을 넣으면 된다.

만약 cmake가 없었다면? 우리는 OpenCV를 활용하기 위해 OpenCV 헤더 정보가 있는  include디렉토리를 직접 찾아서 입력해야 하고, 라이브러리 파일도 직접 찾아서 (아마 Makefile에) 추가해줘야 했을 것이다. 라이브러리 버전이 바뀌거나 업데이트되면? 또 달라진 경로와 파일을 일일이 수정해줘야 한다. 이 과정을 cmake가 알아서 해준다.

여기까지 와서 왜 라이브러리와 인클루드 디렉토리를 추가해야 하는가? 이런 의문이 들면 C컴파일 방법을 처음부터 다시 배워야 한다. 이글 맨 위의 링크에서 1탄으로 가시기 바란다.

참고로 CMake 스크립트에서는 순서가 중요하다. target_xxx 인 명령어는 add_executable이나 add_library 뒤에 와야 한다. 그 밖의 명령어는 add_executable/add_library 앞에 오면 된다. 제일 중요한 부분인데.... 마지막에 밝힌다.



  이상으로 우분투에서 C+ 개발하기 시리즈를 마친다.

--------

VSCode로 빌드하기... 는 그냥 연재 안 하는 것으로...  검색해보면 다른 글이 많으니 굳이 내가 수고할 필요가 없다. Remote SSH로 연결해서 컴파일하고, 디버그하고.. 그런 내용일 것이다.
여러분들이 라즈베이파이, 어디 서버 등 리눅스에서 돌릴 프로그램을 개발한다면 파이썬이든 C++ 이든 무조건 VSCode의 Remote SSH 기능을 활용하는 것을 추천한다.

--------

제 블로그에서 가장 인기있는 글이 바로 여기 시리즈라서 따로 인사를 남깁니다. 방문하신 모든 분들, 댓글 달아주신 분들 감사합니다.


Read More

우분투에서 C++ 개발하기 (2) - Make

우분투에서 C++ 개발하기 (1) : https://ladofa.blogspot.com/2018/07/c-1.html


 1탄을 만들어 놓고 몇 년이 흘렀는가 모르겠다. 2탄을 만들게 될 줄도 몰랐다.

하여튼 이어서 makefile에 대해서 알아본다.

C나 C++을 사용하다 보면 수도 없이 컴파일 하고 빌드하고 이런 일이 반복되는데 매번 라이브러리 경로와 관련된 라이브러리 파일과 기타 등등등을 입력하는 것도 번거롭고 새로 수정한 파일이 무엇인지 따라다니면서 컴파일하는 것도 힘들다. 그래서 리눅스에서는 Makefile이란 툴을 쓴다. 윈도우에서 Visual Studio로 개발하면 이런 거 필요없는데.

Makefile은 빌드에 필요한 스크립트를 텍스트 형식으로 저장한 파일이다. 이 파일은 반드시 이름이 Makefile 이어야 한다. 확장자 없이 이름만 Makefile 이면 된다. 무슨 파일 이름이 Makefile이냐. ...

Makefile 속에 있는 스크립트를 해석해서 실행하는 프로그램은 make이다. 프로그램 이름이 make 다. make를 실행하면 실행한 현재 경로에서 Makefile을 찾아내고 요걸 해석해서 빌드를 수행한다.


우선 다음과 같은 예제를 생각해보자.

<my.h>

int my_func(int x);


<my.cpp>

#include "my.h"

int my_func(int x)

{

    return x * x;

}


<main.cpp>

#include <iostream>

#include "my.h"

int main(void)

{

    printf("%d^2 == %d\n", 3, my_func(3));

}


뭐 이렇게 간단히 구성되어 있다고 하자. 요걸 컴파일 하려면 1탄에서 배운 대로 다음과 같이 입력해야 할 것이다.

$ g++ -c my.cpp

$ g++ -c main.cpp

$ g++ -o test main.o my.o


매번 이렇게 하기 귀찮으니까 이제 Makefile을 이용할 차례이다. Makefile 은 타깃의 집합이다. 타깃은 다음과 같이 서술해야 한다.

[타깃이름]: [타깃에 필요한 파일들]

      [타깃 실행 코드]

여기서 타깃 이름은 그냥 아무 이름이 될 수도 있고, 파일명이 될 수도 있다. 실행 코드를 통해 결과 파일 하나가 확실히 나오는 경우에는 타깃 이름을 파일명으로 한다. 예를 들어

<Makefile>

my.o: my.h my.cpp

    g++ -c my.cpp

이와 같이 Makefile을 작성할 수 있다. 달랑 두 줄이다. 여기서 중요한 것은 두 번째 줄 앞에 있는 공간이 탭 하나이다. 지금 블로그를 작성할 때는 어쩔 수 없이 스페이스를 때려 넣었지만 실제로는 반드시 1탭이어야 한다.

이제 아래와 같이 실행한다.

$ make my.o

그 결과로 my.o 파일이 생성된다. 만약 이미 생성되었다면 up to date. 메시지가 뜰 것이다.


하나의 타깃은 다음 타깃의 재료가 될 수 있다. my.o 그리고 main.o 는 링커에서 실행 파일을 만드는 재료가 된다. 혹은 재료가 필요 없는 명령도 있다. 다음 예를 보자.

<Makefile>

my.o: my.h my.cpp

    g++ -c my.cpp


main.o: my.h main.cpp

    g++ -c main.cpp


test: my.o main.o

    g++ -o test main.o my.o


all: test


clear:

    rm -f my.o main.o test


여기서 my.o 와 main.o 는 test의 재료가 된다. 만약 make test를 했는데 my.o가 존재하지 않는다면 해당 타깃부터 만들고 난 뒤에 test를 만들게 된다. 한 번 더 make test를 실행하면 기존에 모든 것이 이미 있으므로 더 이상 작업을 진행하지 않는다. 파일을 수정하고 나면 수정된 파일을 재료로 하는 타깃만 새로 빌드될 것이다.

all 타깃은 재미있게도 실행 명령이 없고 타깃만 명시되어 있다. make all 을 실행하게 되면 그 재료가 되는 test를 만들 것이다. 만들고 나서 특별히 할 일은 없다.

clear는 반대로 타깃 파일이 없다. 보통은 타깃 파일이 존재해야 그 다음으로 명령문을 수행하게 될테지만 요구사항이 되는 타깃이 없으므로 그냥 무조건 명령을 수행하게 된다.

all, clear, install 이 세 가지 타깃은 모든 Makefile에서 관용적으로 쓰이는 것들이다. 모르는 설치파일이라도 우선 make install 부터 실행해보면 된다.


여기까지 이해했으면 이제 위키피디아에서 제공하는 샘플 Makefile을 살펴보자.

맨 윗줄에 있는 것들은 환경변수이다. 환경 변수의 이름 역시 암묵적으로 정해져 있다. 왤케 암묵적인게 많냐... $(OBJ)을 보면 타깃 여러 개를 한 번에 지정하고 있다. 그리고 이상한 기호들이 보이는데 $@는 타깃 이름, $^는 재료 이름, $<는 재료 중 맨 첫번째 항목을 의미한다. 

대충 이 정도면 원리는 이해한 셈이고, 그 밖에 엄청난 규칙들이 많다. 심지어는 요즘도 버전업이 되고 있다. 나머지는  http://doc.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-4.html 이런 곳에서 자세히 살펴보길 바란다.


요즘은 CMake나 다른 툴을 이용하고 Makefile은 거의 안 쓰기는 하지만 그래도 기본 원리 정도는 어렵지 않으므로 알고 있는 것이 좋다. 임베디드나 기타 작은 소프트웨어에서는 여전히 직접 작성해서 쓰기도 한다.


다음으로는 CMake를 사용한 컴파일 과정을 간단히 살펴볼 것이다.


우분투에서 C++ 개발하기 (3) - CMake
Read More

git 튜토리얼 (1)

들어가면서

Git을 자주 쓰는 사람들은 Git이 대세라고 확신할테고, 심한 경우에는 "요즘 Git도 안 쓰고 개발이 되냐"고 말하기도 한다. 그러나 아직 많은 사람들이 Git에 손을 못 대고 있거니와, 쓰더라도 자세히 모르고 그냥 쓰는 경우도 많다.

Git의 장점은 그냥 강력하다는 것이고, 단점은 개발의 핵심도 아닌 도우미 프로그램 치고는 너무 복잡하다는 것이다. 여기서 한 가지 위안거리가 있는데, Git의 기능 중 한 가지라도 배우면 그건 반드시 유용하게 써먹게 된다. 강력하고 어려운데 쓸데없다면 그게 왠 똥인가 말이다. 그런데 Git은 최소한 다 쓸모있다. 배운만큼 써먹자.

형상 관리 프로그램

형상관리 프로그램을 사용하지 않으면서 버전 관리를 할 수 있는 대표적인 방법은, 프로젝트 통째로 zip으로 압축하고 이름에 날짜를 적어 보관하는 것이다. 과거의 코드를 보고 싶으면 특정한 날짜의 압축을 풀어서 확인하면 된다. 매번 중요한 순간마다 zip으로 압축하는 것이 git에서 커밋(commit)이랑 똑같다.

커밋이란 말은 앞으로 자주 쓸 텐데, 지금은 소스 코드에 대한 청사진, 혹은 스냅샷 정도로 생각하면 좋다. 그냥 소스코드 버전으로 생각해도 된다. 특별히 결정적인 순간마다 저장해서 나중에 두고두고 꺼내보고 비교해볼 수 있다. 한 번 커밋한 내용은 원칙적으로 수정이 불가능하다.

형상관리 프로그램을 이용하면 매번 zip으로 압축해서 소스 코드를 보관할 필요가 없다. 대신 커밋으로 빠르게 코드를 보관하고 복원할 수 있다. 여러 사람이 작업할 때도 간편하게 버전을 관리할 수 있다. 버전 간의 차이점을 확인하고 싶을 때도 편리하게 툴을 이용할 수 있다.

SVN과 차이점

SVN에서는 모든 소스 버전 정보를 서버에서 관리한다. 프로그래머는 서버에 접속해서 특정한 버전의 커밋을 다운받고 편집한 뒤 새 버전으로 서버에 커밋한다. 반면에 Git은 모든 정보를 서버로부터 카피(clone)해서 로컬에 저장한다. 커밋도 로컬에서 한다. 서버와는 단지 sync를 맞출 뿐이다.

SVN이 웹메일이나 클라우드 컴퓨팅처럼 동작한다면, Git은 Google Drive처럼 동작한다.


Git과 GitHub

  GitHub는 Git으로 생성된 저장소를 운영하는 사이트이다. 기존 Git에 사용자 관리, 이슈 관리, 웹 소스 편집 등 기능을 붙여서 만들었다. GitHub 이전에 오픈 소스의 성지로 불리던 곳은 SourceForge인데 SVN으로 운영된다. 사람들은 SourceForge를 즐겨 이용하면서도 한 편으로는 자신만의 저장소를 만들고 싶어했다. 왜냐하면 SourceForge가 없어질 경우 모든 관리 데이터를 잃어버리기 때문이다. 이것은 모든 계란을 한 바구니에 담는 것과 마찬가지이다. 그래서 Google이나 Microsoft 등 굴지의 회사들은 SourceForge 이외에 자신들만의 저장소를 만들어서 운영하고자 했다. 그런데 GitHub가 나타나면서 모든 고민은 사라지고 GitHub 세상이 됐다. 이 세상 거의 모든 오픈소스 프로젝트를 GitHub에 담으면서도 사람들은 예전처럼 불안해하지 않는다. 서버와 동일한 관리 정보가 바로 자신의 컴퓨터에 있기 때문이다.

GitHub를 사용하면서도 한편으로 얼마든지 자신만의 서버를 만들어서 모든 내용을 GitHub와 공유할 수 있다. 로컬 컴퓨터의 내용을 두 군데 서버와 동시에 싱크를 맞추면 그만이다.


도우미 GUI  프로그램

Git은 기본적으로 command line으로 동작하게 만들어졌다. 윈도우 95가 나오면서 GUI 혁명이 이뤄진지 20년이 훨씬 지나가지만 아직도 command line방식을 고수하고 있는 프로그램이 있는데 Git 또한 그렇다고 하겠다.  이를 참다 못한 사람들이 Git API를 이용해서 GUI로 만들어놨다. 이것들은 사용하면 좋긴 한데, 자칫 Git에 대해 오해한 상태에서 계속 쓸 여지도 있다. 하여 우선 본 튜토리얼은 command line으로 진행하고, 나중에 GUI 프로그램도 살펴본다. 핵심 내용만 짚으면 별로 복잡한 명령어도 없다.


지금부터 튜토리얼 시작

이제 진짜 시작이다.

GUI 프로그램을 활용하면 쉬워보이기는 하는데 핵심적인 원리와 내용을 설명하기가 어렵다. 그래서 우선은 커맨드 라인으로 진행하고, 나중에 GUI도 설명할 계획이다. 사실 커맨드 라인으로 다룰 줄 알게 되면, GUI 프로그램은 자동으로 그냥 사용법을 이해하게 된다. 진짜, 뻥 아니고.

위에서 잠깐 언급했듯이 Git은 기본적으로 서버 없이 로컬에서 돌아가는 프로그램이다. 만약 혼자 간편하게 소스코드를 관리할 목적으로 git을 사용한다면 GitHub도 필요 없고, 서버도 필요없이 간단하게 Git만 설치하면 된다.

모든 설명 과정은 윈도우 사용자 기준으로 진행된다. 리눅스 사용자들은 아무래도 알아서 잘 하는 습관이 되있을테고 이런 설명도 필요 없겠지.

Git을 설치하는 방법에는 여러 가지가 있는데, Visual Studio를 설치하다 보면 같이 번들로 설치되기도 한다. 어쨌든 가장 보통의 방법은 직접 다운받는 것이다. 대뜸 깃허브로 들어가면 다운 못 받는다. 거기는 git서비스를 제공하는 곳이고 https://git-scm.com 으로 들어간다. 첫 화면에 바로 다운로드 버튼이 보인다.

설치 과정 중에 default editor를 선택하게 된다. git에서 왠 에디터냐? 파일끼리 병합하고 어쩌고 하는데 에디터가 필요하다. 사실 여기 튜토리얼을 진행할 때는 별 필요가 없으므로 일단 적당히 아무거나 선택하고 넘어가자.

나머지는 그냥 대충 Next를 눌러도 상관이 없을 것 같다.

자, 이제 Git을 시작해보자. Git은 데이터베이스가 필요없다. 모든 소스 관리 정보를 소스코드가 들어있는 그 폴더 속에 저장한다.

커맨드 창(Command Prompt)을 띄우고 대뜸 git이라고 입력해보자.



아무 내용이나 막 나오면 일단 잘 깔렸다. 방금은 git.exe라는 프로그램을 실행한 것이다. 앞으로 이것을 git 프로그램이라 부르겠다.

이제 소스 코드가 들어있는 폴더로 이동한다. 이미 소스 코드가 들어있어도 상관없고, 빈 폴더에서 시작해도 좋다. 어쨌든 폴더로 가야 하는데, 커맨드 입력이 서툴면 아래 그림을 참고한다.



하여튼 드라이브를 옮길 때는 X: 와 같이 콜론을 이용하고, cd 명령어를 통해 폴더로 들어간다. 폴더에서 나오려면 "cd .." 을 입력하면 된다.

우선 나는 빈 폴더를 만들어서 시작할 것이다. 최초의 저장소를 생성하는 명령어는 git init이다. 앞서 말했지만 git init명령은 반드시 빈 폴더에서 할 필요가 없다. 이미 잘 돌아가고 있는 소스를 Git으로 관리하고 싶을 때도 그 시작은 git init이다.





명령이 실행되고 나면 .git 이라는 숨김 폴더가 생성된다. 리눅스에서 '.'으로 시작하는 이름은 무조건 숨김 속성이 되는데, 윈도우에서는 Hidden속성이 따로 부여된다. 폴더 내에 .git 폴더가 있으면 그것은 git으로 관리되는 폴더라고 할 수 있다. git프로그램은 반드시 .git폴더가 보이는 곳에서 실행해야 한다.

.git 폴더에는 Git 관리 정보가 다 들어간다. SVN으로 따지면 서버에 저장되어야 할 모든 정보들이 .git 폴더에 저장되는 것이다. 당연히 용량이 상당한데, 보통 관리되는 소스코드의 1/2 정도 크기이다. 나름의 방식으로 압축이 된다.

Git으로 관리되는 소스 코드를 카피할 때, .git 폴더만 카피해도 된다. 모든 커밋 정보가 .git 폴더에 들어있기 때문이다. 실제 소스 코드는 .git속에 들어있는 커밋을 이용해서 복원하면 그만이다. 서버에서 소스를 다운받을 때(clone) 실제로는 .git 폴더를 다운받는다.

현재 저장소의 상태를 확인하는 명령어는 git status이다.



뭐라고 영어로 나오는데 뭔 말인지 하나씩 살펴보자.

On branch master => 현재 branch를 나타낸다. 지금은 branch를 소스코드의 어떤 갈래, 분기점이라고 이해하자. branch가 아예 없으면 안 되니까 기본으로 하나 생성해주는데, 그 이름이 master이다.

No commits yet => 아직 아무 커밋도 없는 백지상태이다.

nothing to commit => commit할 파일이 없음을 나타낸다. 마지막 커밋과 비교해볼때 내가 작업한 내용이 없다는 뜻이다.

이제 이 폴더에 아무 작업이나 해보자. 여기서는 file.txt를 만들고 아무 내용이나 넣어보자. 기왕이면 두 개를 만들자.

이제 git status 를 다시 입력하면 다음과 같이 나타난다.


Untracked files 라는 문구가 있다. 처음 보는 파일, 혹은 관리되지 않는 파일이라는 뜻이다. 소스 코드 내의 모든 파일은 매 커밋 때마다 변화를 추적하는데, 이 파일은 아예 첨 본다는 뜻이다.

이제 새로 만든 파일을 커밋할텐데, 그러려면 파일을 스테이지에 올리는 작업이 필요하다. 이게 무슨 말이냐? 수정된 파일이 엄청 많은데, 그 중에서 필요한 파일만 꼭 커밋하고 나머지는 임시로 만든 것이니 그대로 두고 싶을 수 있다. 그럴 때 필요한 파일만 스테이지에 올려서 커밋하고, 나머지는 그대로 둔다. 대체로는 소스를 빌드해서 생긴 exe파일이나 기타 임시 파일들은 커밋하지 않고 소스 파일만 커밋한다.

여기 예제에서는 file.txt를 커밋하고 싶으니 이것만 스테이지에 올린다. git add 명령을 이용한다.



스테이지에 올라간 파일은 커밋 대기 상태가 되며, 이 순간 파일의 내용은 임시로 저장된다. 만약 파일이 너무 많으면 "git add --all"로 모든 언스테이지 파일을 스테이지에 올릴 수 있다. 참고로 별표(*)를 이용하는 것도 가능하다.

스테이지에 파일을 올렸는데 지우거나 수정하면 어떻게 되나? 직접 해 보면 안다.


스테이지에 올라간 상태를 기점으로 해서 변경된 내용은 언 스테이지 상태가 된다. 만약 이 상태로 커밋하면 스테이지에 올릴 당시 저장되었던 file.txt의 내용이 커밋된다. 하여튼 스테이지에 올린다는 것은, 커밋할 내용을 고르기 + 임시로 저장하기 정도로 생각하면 된다.

만약 스테이지에 올리고 수정 후 또 올리고 .. 를 반복한다면 ? 스테이지가 여러 개 생기느냐? 그건 아니고 그냥 하나의 스테이지가 계속 누적되어 유지된다.

하여튼 원점으로 돌아와서 file.txt를 다시 만들고 add 명령으로 재차 스테이지에 올렸다, 치고. 이제 진짜 커밋을 해보자.

대충

git commit -m "첫 커밋이다!"

라고 입력하면 아래와 같이 뜬다.


이게 뭔 말이냐, 이메일과 이름이 없다는 뜻이다. 커밋할 때는 작성자, 이메일, 메시지(설명), 이 세 가지 정보가 무조건 들어가야 한다.

그러므로 다음과 같이 입력하면 커밋이 되긴 된다.

git commit -m "첫 커밋이다!" --author="myname <myemail@aaa.com>"

여기서 잠깐, 이름과 이메일은 아무거나 넣어도 되나? 네. 그렇다. 깃허브에 올릴 때도? 아 물론이죠. 이름과 이메일을 남기는 것은.. 사실 그냥 주석의 연장선으로 보면 된다.

다만 GitHub를 이용할 경우, 왠만하면 GitHub에서 쓰는 아이디를 넣어주는 것이 좋다. 나중에 연동되는 기능이 있기 때문이고, 그냥 보기에도 좋기 때문이다. 여러 개의 저장소를 동시에 쓴다면? 아 몰랑..~ 이름 여러 개는 넣을 수 없으니 그땐 뭐 알아서 하는 거지.

하여튼...

매번 이름과 이메일을 넣기 귀찮으니 global 속성으로 정해줄 수 있다. 위의 캡쳐 화면처럼 따라서 입력하면 된다. 좀 길지만 한 번만 하면 되는 거니 참자.

그러고 나서 다시 git commit -m "메시지" 를 입력하면 대망의 커밋이 된다.



git status를 입력하면 아까 초록색으로 스테이지 됐던 것이 사라졌다? 어디갔어? 커밋했으니까 이제 없지. 대신 빨간색 언스테이지 파일은 그대로이다. 이제 git log를 입력해보자.



git log를 입력하면 현재까지의 모든 커밋을 쭉 볼 수 있다. 길어질 경우 나가는 키가 'q'다. 자.. 근데 한글이 깨져 있다! ㅜㅜ 해결 하는 방법이 아예 없는 것은 아니지만 괜히 인코딩만 더 꼬일 수 있으므로 그냥 두는게 좋다. 어차피 GUI 툴을 쓰면 다 해결된다.

.
.
.

다음 튜토리얼에서는 커밋을 실용적으로 활용하는 예 - 수정된 부분 확인, 분기, 병합 등을 해볼 것인데.. 과연 안 귀찮을 수 있을지..


Read More

우분투에서 C++ 개발하기 (1) - 컴파일 과정 및 gcc

이 글은 윈도우에서 Visual Studio만 쓰다가 Ubuntu를 생소하게 생각하는, 나와 같은 사람을 위해 쓴다.

글쓰기에 들어가기 전에 Visual Studio와 같은 통합 환경에 비해 리눅스에서 C++과 같은 언어를 개발한다는 것이 얼마나 복잡한 일인가부터 상기시켜야겠다. C++ 자체가 원래 언어차원에서부터 빌드가 영 까다로운 부분이 있다. 리눅스 사용자는 그 문제를 그대로 정면으로 맞이해야 한다.  그나마 편리하게끔 만든 것이 3편에서 나오는 CMake 정도인데, 이것마저도 복잡한 스크립트를 직접 입력해야 한다.

자유롭고 오픈될 수록 불편하고 복잡하다. 자유도가 높다는 것은 그 자유를 누릴 수 있을 수준으로 공부와 노력을 들여야 겨우 좀 사용할만하다는 뜻이 된다. 


하여튼 글 쓰기의 계획은 이렇다.

1. 맨 땅에 삽질하는 심정으로 g++컴파일러를 직접 이용해본다.
2. makefile을 이용해본다.
3. CMake를 이용해본다.
4. vscode를 활용하여 개발환경을 구축해본다.

그 첫번째, 맨 땅에 삽질하기.


1. C++의 컴파일 과정


VS만 쓰다보면 컴파일러 개념이 희박해지는데, 이는 당연한 것이다. VS라는 훌륭한 툴이 있기 때문에 우리는 아무 것도 신경 안 써도 된다. 원래 정치 선진국일수록 국민들이 정치에 무지하다. 그러나 리눅스를 제대로 이용하려면 컴파일에 대해서 좀 자세히 알아야 한다.

컴파일 과정을 모르는 사람을 위해 최대한 자세히 설명해본다.
컴파일 과정은 다음과 같다.

[소스코드] -> [바이너리] -> [실행파일]

소스코드는 사람이 읽을 수 있는 텍스트 파일이다. 이것을 컴퓨터 명령코드(기계어)로 번역하게 되는데, 이렇게 번역된 결과물을 보통 '오브젝트 파일', 혹은 '바이너리 파일'로 부른다. 두 명칭 모두 약간 애매모호한 점이 있어서 설명해본다.

우선 '오브젝트 파일'이란 말은 컴파일의 목적이 된다는 뜻에서 나왔다. 또 다른 말로 '타겟 파일' 혹은 '오브젝트 코드', '타겟 코드' 이런 말들로 불리는데 다 같은 말이다. 소스의 반댓말이 타겟 아니겠는가.

'바이너리 파일'란 말은 본래 '텍스트 파일'의 반댓말로서, 아스키 코드로 작성되어 텍스트로 읽을 수 있는 파일이 아닌 것들의 집합이다. 즉, 사람이 읽을 수 없는 파일이란 뜻이다. 소스코드는 사람이 읽을 수 있는 텍스트 파일의 일종이다. 반대로 기계어로 번역된-컴파일된 파일은 사람이 읽을 수 있는 텍스트 파일이 아니다. 이런 의미에서 컴파일된 결과물을 바이너리라고 부르는 것이다.

파일은 두 가지로 나뉜다. 프로그램과 프로그램이 아닌 것. 프로그램은 실행이 가능한 명령어로 구성된 것이고, 그렇지 않은 문서파일, MP3등은 프로그램이 아니다. 여기서 바이너리는 큰 의미에서 프로그램으로 봐야 한다. 얘기하다 보니 점점 산으로 간다.

하여튼 우리가 작성한 C++코드는 바이너리(프로그램)으로 번역된다. 바이너리는 여러 개일 수 있다. 그런데 이들은 바로 실행할 수 없다. 이들을 묶어서 OS가 실행할 수 있는 실행파일로 만들어주는 작업이 링크이다. 바이너리는 다시 말해서 프로시져(함수)들의 묶음이다. 이 함수들 중에 main이란 놈이 있다면, 이것을 시작점(엔트리 포인트)으로 해서 프로그램을 실행한다. 그래서 main 함수는 무조건 하나 있어야 하며, 한 개만 있어야 한다.

main이 없으면 바로 실행할 수는 없지만 라이브러리는 될 수 있다. 정적 라이브러리, 동적 라이브러리, 이런 친구들이 될 수 있다는 것이다. 이런 라이브러리들은 main이 없다.

결론적으로 컴파일 작업을 수행하는 컴파일러와 링크 작업을 수행하는 링커는 엄연히 다른 존재다. 그러나 보통 컴파일러라고 하면 링커의 기능도 들어있다.


2. 컴파일러 잡설, gcc


컴파일러는 소스코드를 기계어로 번역하는 작업을 한다고 했다. 그런데 우리는 한 가지 기계(컴퓨터, CPU)만 가지고 있는 것이 아니다.

재미삼아 미약한 지식을 동원하여 컴퓨터의 역사를 살펴보면 옛날에는 컴퓨터란 놈이 표준이 없고 우후죽순으로 각자 자기만의 컴퓨터를 개발해서 쓰곤 했다. 완전히 다른 컴퓨터들이었기 때문에 서로 명령어와 구조가 달랐고, 그래서 각자 컴파일러를 따로 가지고 있었고, 프로그램 호환도 전혀 안 됐다. 그러다 IBM에서 자신들의 CPU 아키텍쳐를 오픈하면서 시장을 거의 점령했고, 이 IBM 구조의 CPU 시장을 점령한 회사가 인텔, 그리고 AMD이다(약 80년대 후반부터). 이 IBM호환 기종에서도 약간 변종이 있는데, 80386 CPU 시절 개발된 x86(32bit) 구조, 그리고 AMD에서 개발한 x86-64(64bit) 구조가 있다. 현재는 이 두 가지 정도 쓰이고 있는데 점점 64bit로 점령되는 추세이다. x86시리즈는 대부분 윈도우 운영체제가 탑재되며 리눅스도 많이 쓰인다. MacOS도 x86으로 갈아탔다.

여기까지는 일반적인 데스크톱 컴퓨터의 얘기인데, 임베디드 - 소형화된 컴퓨터에서는 사정이 좀 다르다. 아직도 다양한 경쟁사들이 저마다의 CPU 아키택쳐로 경쟁하고 있는 형국이며, 그 중에서도 단연 1위는 ARM이다. 스마트폰에 들어가는 CPU가 대부분(내가 알고 있는 전부는) ARM으로 되어 있고 라즈베리파이도 ARM이다. 운영체제는 전부 리눅스(안드로이드)이다. ARM은 버전 6부터 시작해서 현재 8까지 나와있다. 

컴파일러는 CPU구조, 그리고 운영체제에 따라서 달라진다. 그 말인 즉슨, 프로그램을 하나 만들었을 때, 이 프로그램이 실행될 수 있는 CPU, 그리고 운영체제가 정해져 있다는 것이다. 

여러분들이 CPU를 하나 만들었다고 하자. 그러면 그 CPU를 동작시킬 수 있는 예제 코드가 필요할 것이다. 그리고 그 코드는 보통(100%) C언어가 된다. C언어로 Hello World를 만든 뒤, 이것을 내가 만든 CPU 명령어로 번역할 수 있는 컴파일러가 필요하다. 그래서 CPU를 만들면 반드시 컴파일러도 같이 만들어줘야 한다. 이 컴파일러 프로그램을 따로 만들어서 사람들에게 나눠줄 수도 있지만, 그냥 다들 컴파일에는 gcc를 쓰고 있으니, 내 CPU에 대한 컴파일 기능을 gcc에 추가한다.

gcc는 모든 리눅스에서 공통으로 쓰는 컴파일러 모음을 뜻한다. 더 멀리는 원래 GNU프로젝트를 시작하기 위해 만들었다는데, 알게 뭐냐. 하여튼 컴파일러 = gcc이다. gcc는 리눅스 운영체제위에서 실행된다는 가정아래 x86, x86-64, armv6, v7, v8 모두를 커버할 수 있다. 그 외에도 듣도 보도 못한 CPU까지 커버한다. 앞서 설명했듯이 모든 CPU 제조사가 gcc에 자기 CPU를 추가하기 때문이다. 아래 링크를 참고하자. 


그래서 결론 : 컴파일러에는 gcc만 있는 것은 아니지만 그런데 gcc면 왠만큼 다 해결이 된다는 것이다.

크로스 컴파일이란 빌드 머신과 타깃 머신이 다른 경우를 말한다. 빌드 머신이란 현재 컴파일이 수행되고 있는 컴퓨터이다. 타깃 머신이란 컴파일 결과물이 실행될 컴퓨터이다. 안드로이드의 경우가 가장 대표적이다. 안드로이드 폰에서 돌아가는 프로그램을 안드로이드에서 개발하는 경우는 거의 없다. 휴대폰에서 엄지손가락으로 코딩하고 그걸 빌드하는 사람이 있겠는가? 보통은 리눅스나 윈도우에서 개발을 해서 apk까지 빌드하고, 이 파일을 안드로이드에 보낸다. apk가 실행되는 타깃머신은 안드로이드인데, apk를 만들어낸 개발 머신은 윈도우이다. 이런 경우를 가리켜 크로스 컴파일이라고 한다. 라즈베리 파이나 그보다 작은 소형 임베디드 보드는 거기서 직접 컴파일 하고 디버깅하고 어쩌구 하기가 매우 불편하니까 대개 크로스컴파일을 하게 된다.

그럼 자바, C#, 파이썬은 어떻게 동작하는가? 이들 언어는 동작하는 방식이 C와 꽤 다르다. 너무 산으로 가면 안 되니 생략.

g++은 gcc의 일부분으로서 C++언어의 컴파일러이다. 요즘 나오는 우분투에는 기본으로 깔려 있다. 여기서 재미있는 사실은 gcc는 C언어의 컴파일러인데 이건 C++로 만들었다. 마인크래프트에 보면 손으로 나무 깎아서 나무 곡괭이 만들고 그걸로 돌캐서 돌곡괭이 만들고 그걸로 철 캐서 철도끼 만들고 철도끼로 나무 캐고 그렇게 하듯이 컴퓨터 언어의 세계도 비슷하다. 포트란으로 C컴파일러 만들고 C컴파일러로 C++컴파일러 만들고 그걸로 다시 C컴파일러 만들고 그걸로 파이썬 만들고 파이썬으로 파이썬 컴파일러 만들고 ...

만약 gcc가 안 깔려있으면 깔아줘야 한다. 우분투에서는 C 언어 프로그래밍 개발에 필요한 것들을 패키지로 묶어서 배포한다.


$sudo apt install build-essential

요렇게 입력하면 gcc랑 make랑 cmake 등등 필요한 것들이 설치될 것이다.

3. 간단한 빌드

드디어 잡설을 끝내고!

본격적으로 개발에 들어가보자.

대충 폴더 하나를 만들고, 그 속에 main.cpp 파일을 작성한다.

<main.cpp>
#include <iostream>
int main()
{
    std::cout << "U**** F***** UBUNTU!\n";
    return 0;
}

다음과 같이 실행해본다.


g++ -c main.cpp #main.o 파일 생성

이렇게 하면 main.o 파일이 자동으로 생성된다. main.o는 앞서 말한 오브젝트 파일 : 바이너리다. 바이너리 파일은 바로 실행할 수 없다. 메인 함수가 물론 포함되어 있지만 그래도 곧바로 실행할 수는 없고, 리눅스가 실행할 수 있는 형식의 파일로 만들어야 한다.


g++ -o test main.o #main 파일 생성
./test #실행

main.o 파일이 바이너리 파일이고, 확장자가 없는 test 파일이 실행파일이다.

하나의 c/cpp 파일은 하나의 바이너리를 생성한다. 그런데 만약 파일이 여러개라면 어떻게 될까? 아래와 같이 파일을 만들어보자.

<my.h>
int myfunc(int val);

<my.cpp>
#include "my.h"
int myfunc(int val)
{
    return val + 1;
}

<main.cpp>
#include <iostream>
#include "my.h"
int main()
{
    std::cout << "calling up myfunc : " << myfunc(3) << std::endl;
    return 0;
}

이제 다음과 같이 명령어를 입력한다.


g++ -c main.cpp

컴파일러는 main.cpp파일을 들여다보고 my.h 파일을 현재 디렉토리에서 찾아낸다. my.h에는 myfunc에 대한 정의가 있으므로 컴파일에는 문제가 없다. main.o 파일이 생성된다.


g++ -c my.cpp

myfunc의 구현이 컴파일되어 my.o 에 담긴다. 이제 main.o와 my.o를 묶어서 test라는 실행 프로그램을 만들면 된다.


g++ -o test main.o my.o

이 실행의 결과로 test파일이 생성된다. 오브젝트 파일이 더 많을 경우에는 줄줄이 다 갖다 붙이면 된다.

여러 개의 바이너리 파일 중 main함수는 단 한개만 있어야 한다. 두 개 있거나 없으면 실행파일이 안 만들어진다.


이 쯤 되서 헤더파일이 무엇인지, 컴파일과 링크가 무엇인지 다시 한 번 생각해보자. 헤더 파일은 함수의 스펙을 적어놓는 것이다. myfunc 함수를 이용해야겠는데, 그 함수가 입력값이 어떤지 출력은 어떤 타입인지 알 수가 없다. 그래서 이 함수는 이렇소이다~ 하고 소개해놓은 것이 헤더파일이다. main.c에서는 헤더 파일의 정보만 보고 프로그램이 잘 돌아갈 지 생각해본다. 헤더에 의하면 입력은 int 한 개이고 출력도 int이다. 문법적으로 문제가 없으면 일단 OK, main은 컴파일이 가능하다.

컴퓨터 조립을 할 때, 부품을 하나의 공장에서 생산하지 않는다. CPU는 인텔에서 만들고 메인보드는 MSI에서 만들 때, 서로 통일된 스펙과 인터페이스만 맞춰놓고 각자 만든다. 나중에 컴퓨터 조립을 할 때, 각자 만든 부품을 연결해서 완성시킨다. 컴파일 과정도 마찬가지인데, 두 개의 바이너리가 각자 빌드된다(컴파일). 서로의 인터페이스에 대한 정보는 헤더파일을 참고한다. 나중에 링커를 통해서 그 둘을 연결(링크)하면 비로서 하나의 프로그램이 된다.


4. 라이브러리 참조


우리가 앞서서 #include <iostream> 이라고 작성했을 때, 무슨 일이 일어났는지 다시 생각해보자. iostream은 헤더 파일 이름이다. (확장자가 아예 없다.) 이 파일은 내 시스템 어딘가에 위치해 있는데, 보통은 /usr/include/c++/4.xxx/ 디렉토리에 들어있다. 본래 어떤 헤더 파일이든지 include 하려면 그 헤더파일이 들어있는 경로를 컴파일러에 알려줘야 한다. 하지만 왠만한 프로그램에서 다 쓰는 이런 스탠다드 라이브러리는 미리 디폴드 경로가 등록되어있다. 이것을 확인하고 싶으면 다음과 같이 명령어를 입력해본다.


g++ -v

혹은


export CPLUS_INCLUDE_PATH

이제 iostram파일을 포함하여 컴파일 하는 데는 문제가 없다. 하지만 링크 과정에서 iostream에 있는 함수와 클래스들이 어디에 어떤 파일로 로 구현되어있는지를 알려줘야 한다. 이것도 디폴트로 등록되어 있다.
우선 라이브러리 파일(바이너리)의 이름은 libstdc++.so.6 이고 파일의 위치는 대체로 /usr/lib 이다. 이 디렉토리는 환경변수 $LD_LIBRARY_PATH에 등록되어 있다.

지금까지 봐왔던 .o 파일이 아니라 .so 인 이유는 동적 라이브러리이기 때문이다. 동적 라이브러리는 윈도우로 치면 dll파일이다. 정적 라이브러리는 링크할 때 직접 그 내용이 실행 파일 안에 복사된다. 그래서 바이너리 파일 크기만큼 실행 파일의 크기가 늘어난다. 반면에 동적 라이브러리는 프로그램에 포함되지 않는다. 대신 프로그램 실행할 때 위치만 알려주면 된다. 그래서 실행파일의 크기가 늘어나지는 않지만, 실행파일 단독으로 프로그램 실행이 안 되고, 꼭 동적 라이브러리를 옆에 붙여줘야 한다. 여러 프로그램에서 동시에 쓰는 바이너리 파일이 있다면 동적 라이브러리가 되는게 유리하다.

만약에 다른 라이브러리를 추가하고 싶으면 다음 세 가지를 컴파일러에 알려줘야 한다.

*관련 헤더파일의 검색 경로
*관련 라이브러리 검색 경로
*관련 라이브러리 파일 이름

헤더파일의 경로에는 -I(대문자 아이) 옵션,  라이브러리 검색 경로에는 -L옵션, 라이브러리 파일 이름에는 -l(소문자 엘) 옵션을 준다. 누구야 첨에 이거 만든 사람 왤케 헷갈리게 I랑 l이랑 섞어놨어..

myfunc을 라이브러리화 해서 추가한다면 다음과 같이 명령어를 입력한다.

#my를 컴파일하여 my.o를 생성한다.
g++ -c my.cpp


#my.o를 묶어서 static library로 만든다. (libmy.a 생성)
ar rcs libmy.a my.o

여기서 ar은 오브젝트 파일을 정적 라이브러리로 만들어주는 툴이다. 오브젝트 파일과 정적 라이브러리는 사실상 차이가 없으며 형식상 약간 다를 뿐이다. 정적 라이브러리란 오브젝트 파일들의 집합이라고 표현할 수도 있으므로 zip으로 묶어주는 것과 비슷한 개념으로 생각해도 좋다.
또한 여기서 라이브러리 이름에 lib라고 붙인 것은 gcc에서 관습적으로 쓰는 표현인데 꼭 지켜야 한다. 해당 파일이 라이브러리임을 나타낸다. 확장자가 a라는 것만 봐도 라이브러리임이 명확한데 굳이 파일 이름에 prefix까지 붙이는 건지 나로서는 이해할 수가 없다.

이제 다음과 같이 입력한다.

#실행파일 'test' 생성
g++ -o test main.cpp -lmy -L./

여기서 -l, -L옵션과 뒤에 나오는 인자 사이에 공백을 두지 않음에 유의한다. libmy.a 파일에서 앞의 lib와 확장자 .a는 빼야 한다. 기호 "./"는 현재 경로라는 뜻으로서 libmy.a 파일이 들어있는 경로를 밝혀줘야 한다. l은 숫자 1이 아니라 소문자 알파벳 k다음에 나오는 l 이다.

동적라이브러리 만드는 과정은 더 쉽다. 그건 알아서 찾아보자.. ㅎㅎ

라이브러리 한 두개 정도는 이런 식으로 추가가 가능하지만 일반적인 프로젝트는 수십개의 오브젝트파일과 수십개의 라이브러리를 묶어서 컴파일하기 마련이다. 이러한 컴파일 옵션을 간소화하기 위해 makefile이 생겨났고, makefile도 작성하기 힘들어서 CMake와 같은 빌드 툴이 개발됐다. 다음 화에서 차차 살펴볼 것이다.


우분투에서 C++ 개발하기 (2) - Make

우분투에서 C++ 개발하기 (3) - CMake
Read More

Visual Studio 2017 + 파이썬 + Tensor Flow 셋팅

Windows에서 파이썬이 돌아간다는 사실은 모두가 알면서도 막상 딥러닝 프레임워크를 돌릴 때는 리눅스를 사용하게 된다. 프레임워크를 사용한 수많은 예제들이 모두 리눅스 기반으로 돌아가기도 하거니와 왠지 윈도우 파이썬은 뭔가 호환이 안 될것 같은 기분 탓이다. 요즘은 파이참(PyCharm)이라는 좋은 개발 환경도 있고, Visual Studio에서도 파이썬을 기본으로 서포트해주기 때문에, 윈도우에서도 특별한 고생 없이, 어쩌면 리눅스보다 더 편하게 파이썬을 사용할 수 있다.

그러나 시작은 언제나 두려운 법. 파이썬 초보들도 쉽게 Tensor Flow를 셋팅할 수 있도록 튜토리얼을 작성해본다.

우선 Visual Studio를 깔아야한다. 2017버전을 설치할 때, Python 개발 환경을 체크해야 한다. 혹시 설치할 때, 체크하는 것을 놓쳤다면 나중에라도 Visual Studio Installer를 실행해서 추가 설치가 가능하다.


여기서 파이썬을 선택한 뒤 수정 버튼을 누른다.

설치를 진행하면 VS의 파이썬 코딩 툴 뿐 아니라 파이썬 자체가 새로 설치된다. 이미 기존에 파이썬이 설치되어 있더라도 마찬가지이다. 여기서 의문이 드는 점은 여러 개의 파이썬을 설치할 경우 어떻게 되느냐 이다. 파이썬은 다양한 버전이 존재하고, 사람마다, 소스마다 써야 하는 버전이 다르기 때문에 자연스럽게 여러 가지 버전을 모두 설치하게 된다. Visual Studio 역시 이러한 파이썬의 사정을 잘 알고 있기 때문에, 설치된 파이썬 버전을 직접 관리할 수 있도록 기능이 제공된다.

개별 파이썬 버전은 폴더 단위로 관리된다. 설치 폴더 밑에 python.exe가 있고, 하위 폴더로 Scripts, Lib 등이 있다.


여러 개의 파이썬이 설치될 때는 위와 같은 폴더가 여러 개 생성된다.

이제 VS에서 파이썬 프로젝트를 만들어보자.

파이썬 프로젝트를 선택하면 된다.




솔루션 탐색기에서 Python 환경을 보면 Python 3.6 (64-bit) 라고 되어 있고 바로 뒤에 (전역 기본값) 이라고 되어 있다. 새 프로젝트를 만들고 나서 특별히 환경을 지정하지 않았으므로 기본값이 지정된 것이다. 특별히 다른 파이썬 버전으로 현재 프로젝트를 구동하고 싶다면 환경에서 오른쪽을 눌러서 고를 수 있다.

이쯤에서 소개할 것은 아나콘다이다. 아나콘다는 파이썬 배포판 중 하나라고 할 수 있다. 예를 들어 리눅스가 래드햇, 페도라 등 다양한 배포판을 가지고 있는 것과 같다. 리눅스 배포판이 핵심 커널 뿐 아니라 다양한 유틸리티를 포함하고 있는 것처럼 아나콘다도 자주 쓰는 라이브러리를 미리 탑재하고 있다.

파이썬 환경에서 중요한 포인트 중 하나는 각각의 파이썬 환경마다 다른 라이브러리를 설치할 수 있다는 것이다. 파이썬에서 설치한 라이브러리는 Lib 폴더에 저장된다.

파이썬에서는 라이프러리를 패키지 단위로 관리한다. 패키지란 라이브러리 묶음으로 생각하면 될 것이다. 예를 들어 우리가 OpenCV를 설치할 때, 각각의 lib파일을 개별적으로 다운받는 것이 아니라 OpenCV라는 묶음으로 설치한다. 이렇듯 패키지란 개념은 이미 일상에서 자연스럽게 사용하고 있다. 파이썬에서는 패키지 다운로드를 위한 툴을 기본적으로 탑재하고 있는데, 그것이 바로 pip이다. pip로 다운받은 패키지는 파이썬 폴더/Lib/site-packages 폴더에 저장된다.

Visual Studio를 사용하면 pip를 좀 더 쉽게 사용할 수 있다. 메뉴에서 [도구]->[Python]->[Python 환경]으로 들어가면 조그만한 도킹 창이 뜬다. 맨 위에서 원하는 환경을 선택한다. 아나콘다를 설치했거나, 기타 다른 버전의 파이썬을 설치했다면 해당 환경을 고를 수 있다. 만약 설치한 환경이 보이지 않는다면 수동으로 추가해야 한다.

 가운데 콤보박스에서 패키지(PyPI)를 고른다.



설치된 패키지가 나타나는데, 특별히 다른 것을 설치하지 않았다면 달랑 pip, setuptools 정도 보일 것이다. X표를 누르면 패키지를 지울 수 있고 화살표를 누르면 최신 버전으로 업데이트한다.

패키지 검색 창에 tensorflow라고 입력해보자. 엄청나게 많은 항목이 뜨는데 대부분의 것들은 텐서플로우를 보조하는 툴이거나 실험 버전 등이고 중요한 것은 그냥 'tensorflow', 그리고 'tensorflow-gpu' 이다. 'tensorflow-gpu'는 말 그대로 GPU 버전이고 그냥 tensorflow는 CPU only 버전이다. 둘 다 설치하면 어떻게 될까? 조금 꼬이긴 하는데 마지막에 설치된 버전이 적용된다. Keras를 사용하면 내부적으로 tensorflow를 참조하는데, 여기서도 마지막에 설치된 버전을 참조한다.





일단 CPU 버전인 'tensorflow'를 설치해보자. 클릭 한 번이면 설치가 시작되고, 설치 과정은 [출력] 창에 표시되는데, 이게 아무래도 좀 불친절하다. 프로그래스 바, 혹은 진행중 표시가 없으니 설치가 되는 건지 됐다는 건지 안 됐다는 건지 알 수가 없다. 하여튼 설치됨 메시지가 뜨면 완료된 것이다.


설치 과정을 잘 읽어보면 알겠지만 필요한 라이브러리들은 자동으로 설치된다. 만약 설치가 안 된다면 pip 버전을 업데이트 한 후 다시 설치해보자.



설치가 끝났으면 이제 헬로 텐서플로우를 외쳐볼 시간이다. 솔루션에서 .py 파일을 하나 선택한 뒤 다음과 같이 입력한다.

import tensorflow as tf

hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

참고로 솔루션에서 진하게 표시된 파일이 F5를 누를 때 처음 시작될 파일이다. 파이썬은 Matlab과 마찬가지로 메인함수가 없고 아무 파일이나 골라서 먼저 실행할 수 있다.

2018-04-27 08:34:58.484009: I T:\src\github\tensorflow\tensorflow\core\platform\cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
b'Hello, TensorFlow!'
Press any key to continue . . .

위와 같이 메시지가 뜨면 성공이다. 맨 윗 줄에 Your CPU supports 어쩌구 하는 경고 메시지는 무시해도 된다. 원래 Tensorflow를 최적화하려면 CPU에 맞게 다시 빌드해야 하는데, 현재는 범용으로 컴파일된 라이브러리를 쓰고 있기 때문에 나타나는 메시지이다.

여기까지 진행되면 CPU 버전 텐서플로우 설치가 완료된 것이다. 이제 tensorflow-gpu를 설치해보자. GPU버전은 사용하려면 CUDA를 지원하는 NVidia 그래픽 카드가 있어야 하고, CUDA, cudnn 설치도 해야 하서 좀 복잡하고 귀찮지만 CPU버전에 비해 거의 20배 빠르기 때문에 충분히 해볼만한 가치가 있다. 사실 GPU가 아니면 딥러닝은 시작되지도 못했다.

Tensorflow 버전에 따라서 요구하는 CUDA, cudnn 버전이 다르다. 확실히 하려면 Tensorflow 홈페이지를 방문해서 확인해보면 된다. 영 귀찮으면 그냥 최신 버전을 설치하면 그냥 저냥 잘 될테지?? 라고 생각하면 오산이다. 텐서플로우 버전에 정확히 맞는 것을 찾아서 설치해야 한다. 현재 tensorflow 1.7 버전은 CUDA 9.0, cudnn 7.1.x 에서 동작한다.


우선 CUDA를 설치해야 하는데, 구글에서 cuda를 검색하면 자연스럽게 다운로드 페이지로 이동한다. 자신의 OS를 잘 선택하고 windows버전을 골라서 다운로드한다. 설치 과정은.. 그냥 next만 누르면 알아서 잘 설치된다. 설치 도중에 화면이 깜빡거릴 수 있으며 단순히 다음을 눌러서 간편 설치를 할 경우 그래픽 드라이버도 새로 설치된다.

다음은 cudnn을 설치해야 하는데, 우선 다운로드 하려면 NVidia ID로 로그인을 해야 한다. ID가 없으면 새로 만들고 로그인한다.



로그인 후 버전을 선택한다. 자신의 CUDA 버전에 맞는 것으로 잘 골라서 다운로드 하면 된다.

CUDA는 설치 프로그램이 알아서 설치를 했지만 cndnn은 받아보면 그냥 압축 파일이다. 아무곳이나 편리한 곳에 풀어놓은 뒤, 설치경로/cuda/bin 폴더를 PATH로 잡아줘야 한다.

혹시 PATH 잡는 법을 모르는 사람들을 위해 안내까지 만들었다~ 시작메뉴에서 제어판을 검색해서 실행한 뒤 아래처럼 폴더 경로를 넣는다. 캡쳐하기 귀찮아서 한꺼번에 모아서 캡쳐 ㅎㅎ




변경된 환경변수를 적용하기 위해서는 재로그인/혹은 재부팅을 해야 한다.

이제 Python pip에서 tensorflow-gpu를 다운로드하고 실행해보면 된다. 다운로드하는데 20분이 걸릴 수도 있으니 천천히 기다려보자.


2018-04-27 23:38:33.037407: I T:\src\github\tensorflow\tensorflow\core\platform\cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
2018-04-27 23:38:33.783060: I T:\src\github\tensorflow\tensorflow\core\common_runtime\gpu\gpu_device.cc:1344] Found device 0 with properties:
name: GeForce GTX 965M major: 5 minor: 2 memoryClockRate(GHz): 1.15
pciBusID: 0000:01:00.0
totalMemory: 2.00GiB freeMemory: 1.64GiB
2018-04-27 23:38:33.789763: I T:\src\github\tensorflow\tensorflow\core\common_runtime\gpu\gpu_device.cc:1423] Adding visible gpu devices: 0
2018-04-27 23:38:34.209099: I T:\src\github\tensorflow\tensorflow\core\common_runtime\gpu\gpu_device.cc:911] Device interconnect StreamExecutor with strength 1 edge matrix:
2018-04-27 23:38:34.213833: I T:\src\github\tensorflow\tensorflow\core\common_runtime\gpu\gpu_device.cc:917]      0
2018-04-27 23:38:34.216125: I T:\src\github\tensorflow\tensorflow\core\common_runtime\gpu\gpu_device.cc:930] 0:   N
2018-04-27 23:38:34.218824: I T:\src\github\tensorflow\tensorflow\core\common_runtime\gpu\gpu_device.cc:1041] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 1405 MB memory) -> physical GPU (device: 0, name: GeForce GTX 965M, pci bus id: 0000:01:00.0, compute capability: 5.2)
b'Hello, TensorFlow!'
Press any key to continue . . .

위와 같이 나오면 성공 ^^

Read More

gogs 윈도우 설치 따라하기

  모든 개발자가 만능은 아니다. 나는 영상 처리는 잘 하지만 안드로이드 앱은 못 만들고, 리눅스는 sudo랑 ls밖에 모른다. 맥도 써 본 적이 없다. 프로그래머라고 해서 한글 단축키를 다 외우고 다니는 것도 아니고 컴퓨터 맞출라면 얼마 드는지 줄줄 꿰고 있는 것도 아니다. 심지어 카톡에서 동영상을 보내면 그게 와이파이로 나가는지 KT 전화요금이 나가는지 모르는 사람도 있는데 그 분은 컴공과 교수님이었다. 그리고 내가 경고하는데, 알집 쓴다고 무시하지 마라!

  개발자는 항상 새로운 것을 익혀야 하는 직업인데, 그걸 좋아하는 사람은 또 많지 않다. 대체로는 제발 하던 대로 했으면 좋겠다는 주의다. 모든 개발자는 귀찮은 것을 싫어한다.

  그래서 gogs가 나온 것이다! git lab을 이용하여 git 서버를 설치하는 것이 워낙 귀찮은 일이거니와 윈도우가 아닌 리눅스에 설치해야 한다. 이런 거 다 필요없고 윈도우에 간단하게 설치할 수 있는 git 서버, gogs다. gogs는 GoLang이 돌어가는 모든 환경에 설치가 가능하다. 리눅스에서도 git lab보다 훨씬 쉽게 설치할 수 있다.

  gogs를 보면서 생각난 것이 trac다. trac는 'SVN + 이슈 트래커 + 위키'를 단 한 번의 실행으로 설치해준다. 웹서버와 디비까지 몽땅 한꺼번에 말이다. 그것도 윈도우에! gogs는 trac에 비하면 다소 불편하다, 아직도 많이 불편하다. DB도 직접 깔아야 하고, 이런 저런 설정도 필요하다. 아 이것마저 찾아보고 고민하기 귀찮다 하는 분들을 위해, 그래서 내가 이 글을 쓰는 것이다.

  열심히 설명했음에도 불구하고 컴맹에게는 다소 어려운 부분이 있다. gogs가 그렇게 생겨먹었으니 어쩔 수 없다, 용서하시라.

  일단 gogs를 다운받는다. 아래 주소이다. LOCAL : ZIP 이라고 써 있는 부분에서 ZIP을 클릭한다.

https://gogs.io/docs/installation/install_from_binary

글을 쓰는 2016년 10월 현재 최신 버전은 0.9.97이다. 64bit기준으로 간다.



  압축을 풀어보면 gogs라는 파일이 있다. 그렇다, 포터블인 것이다. 아무 곳에나, 아니 아무 곳 말고 D:\gogs 라든지 편리한 곳에 카피한다. 포맷하고 다시 설치하기 귀찮으니 c에는 깔지 말자.

  gogs.exe를 실행해보면 뭔 창이 획 지나간다. 바로 다음과 같은 화면이 지나간 것이다.



  그렇다, 실행하려면 뒤에 파라미터를 넣어야 한다. 다른 것은 잘 모르겠고, git과 연동되는 웹서버를 띄우려면 'gogs web'을 실행한다. 그런데 매번 gogs web이라고 타이핑하기 귀찮으니까 bat 파일을 만든다.

  bat파일을 만드는 방법은? 우선 gogs.txt 파일을 만든 뒤, 내용에 단지 'gogs web'이라고 써놓자. 메뉴에서 파일 확장자 보기를 체크하면 .txt라고 보인다. 여기서 txt를 bat로 바꿔서 gogs.bat파일을 만든다.





  하 괜히 모든 개발자는 만능이 아니고 어쩌구 썰을 풀어논 바람에 이런 것까지 그림 설명을 하고 앉았다 ㅜㅜ

  생성한 bat파일을 실행해보면 아래처럼 뜬다. listen 어쩌구 나오면 성공.



  웹 서버가 열렸으니 접속을 해보자. 인터넷 창을 켜서 주소에 127.0.0.1:3000이라고 쓴다. 감격스런 첫 화면이 열린다.



  딱 보면, 생각나는 게 있다. 제로보드. 지금은 ex엔진이던가? 하여튼 거기서처럼 최초 실행 시에 DB 설정을 해줘야 한다.

  DB를 깔아보자, MySQL. 커뮤니티 버전이 무료이다.

https://dev.mysql.com/downloads/installer/




  위의 주소로 들어가서 아무거나 받으면 된다. 혹시 설치하다가 중간에 꺼지고 아무 것도 안 뜰 수 있다. 당연하다, 우린 인스톨러만 설치했지, 본격적인 인스톨은 아직 안 한 거거든. 시작 메뉴를 살펴보자. MySql Installer를 실행하면 된다.




그런데...



이건 MySQL 자동 업데이트를 하겠냐는 거다. 묻지마 쫌! 에 체크하고 NO.



  우리는 개발자니까, 개발자 디폴트!



  gogs를 그냥 자기 메인 컴퓨터에 까는 사람은  Development Machine을 고르고, 서버 컴퓨터에 까는 거면 Server Machine을 고른다. Dedicated Machine는 DB전용 컴퓨터에서만 선택한다.



  여기서 입력하는 패스워드는 절대 까먹지 말고 어디 적어놓길.



  윈도우 서비스로 등록할거냐 묻는 거고, 시작할 때마다 실행할 거냐고 묻는 거다. 위에서 Development Machine을 선택했으면 부담없이 Next를 눌러도 좋다. Server Machine을 선택했으면 서버니까 당연히 그냥 Next.



  이건 모르니까 Next.



  다왔다. 그냥 Execute. Next! Finish! Execute! 어서 넘어가라.

  자동으로 워크벤치가 뜬다. 디비 설정 쉽게 하는 UI 프로그램이다.


여기서 첫 번째 Local instance를 선택한다.



  난 왜 8.0 버전을 설치한 거지?? 하여튼 Continue Anyway 하여튼.



여기서 일단 눈여겨봐야 할 점은 디비가 저장되는 곳이다. C:\.. 에 저장되어 있는데 나중에 포맷해서 날릴 걱정이 되면 장소를 옮겨야 한다.

http://blog.opid.kr/361

여기 블로그에 잘 설명이 되어 있으니 따라해보시길.



  이제 gogs를 위한 스키마를 만든다. 스키마가 DB고 DB가 스키마다. 아래 그림처럼 빈 공간에 오른쪽 버튼을 눌러서 만든다. 이름은 gogs로 하고 Apply, Finish, Next! Execute!




   이제 MySQL은 끝이다.

  원점으로 돌아와서, 127.0.0.1:3000으로 다시 접속해보자. 신경써야 할 곳은 두 군데다.


  DB 패스워드는 Workbench에서 쓰던 그거다. Repository Root Path는 C 말고 다른 곳으로 정하는 것이 좋겠다.
  나머지 항목들은, 위에서 시키는대로 해왔으면 건드릴 필요가 없다.  감격의 Install Gogs 버튼을 누르면 한참 설치가 된다. DB를 생성하느라고 오래 걸린다.

  근데 문제는.. 내가 설치가 안 되는 것이다. 설치 후 gogs가 재시작되는데, gogs 자체가 실행이 안 된다. 왠지 MySQL문제 같아서 8.0을 지우고 5.7을 다시 까는 삽질을 한 끝에 다시 진행하니 되네. 8.0 버전을 내가 어디서 구해서 깔았는지 기억이 안 난다. 괜히 삽질.


어쨌든 이 상태가 되면 설치는 끝난 것이다. 첫 등록하는 아이디는 자동으로 관리자가 된다. Sign up now!



이 다음부터는 알아서 건드려보기 바란다! ㅎㅎ

근데, gogs가 계속 실행상태여야만 한다. 창을 계속 띄워놔야 함은 물론이고, 귀중한 테스크바 공간을 차지하고 있어야 한다.



  요걸 MySQL처럼 서비스로 만드는 방법이 있다.

https://gogs.io/docs/installation/run_as_windows_service

  위의 링크를 참고한다. 특급으로 설명이 잘 되어 있다 - 영어로. 설명이 구 버전 중심으로 되어 있지만 대충 따라해도 잘 된다.
Read More
Powered by Blogger.