물론, Jenkins Docker 이미지를 사용하면 설치 과정없이 보다 쉽게 자동 빌드 환경을 구성할 수 있다.
하지만, 서버 하나에 [Jenkins 빌드 환경] 과 [Compile 환경] 을 모두 구성해야 한다면, Jenkins를 직접 설치하여 사용해야 할 경우가 생긴다. (특히, C나 C++같이 컴파일이 필요하고, 빌드 환경의 영향을 많이 받는 프로그램을 빌드할 경우에는 더욱 그렇다.)
이번 글에서는 Jenkins 빌드 환경을 구성하기 위한 첫 단계로 Linux OS를 설치해보자.
1. Linux 설치
Virtual Box에 VM을 하나 생성하고, CentOS 7.9 Minimal 이미지로 부팅을 한다.
부팅 후 언어 선택 화면이 나오면 설치 언어를 선택한다. (영어로 선택해도 무방)
언어를 선택했으면, 아래의 항목들을 목적에 맞게 선택한다.
- 날짜와 시간 : 아시아/서울
- 소프트웨어 선택 : 최소 설치
- 설치 대상 : 자동 설정 혹은 수동으로 파티션 설정
항목들을 선택했으면, [설치 시작]을 누른다.
설치가 진행되는 동안 ROOT 암호를 설정한다. (사용자 생성은 필요시 생성)
설치가 완료되면, [재부팅] 버튼을 눌러 시스템을 재시작한다.
OS가 정상 설치되었으면, 부팅시 아래처럼 OS 선택 화면이 출력된다. (처음 항목을 선택)
처음 항목을 선택하여 부팅하면, 로그인 화면이 출력된다.
설치시 설정한 root 사용자 패스워드를 사용하여 로그인한다.
2. Linux 설정
아래의 설정들은 보안 관련 설정을 해제하는 설정으로, 보안 설정을 사용중이라면 그대로 유지해도 된다.
(1) SELinux 사용해제
$ vi /etc/selinux/config
SELINUX=enforcing 으로 되어있는 부분을 SELINUX=disabled 로 변경한 후 저장
- 복사로 캡처된 변수는 기본적으로 constexpr 타입으로 지정되어 값의 변경이 불가하다.
- mutable로 선언하면 값의 변경이 가능하다.
int var1{0};
[var1]() {
var1 = 999; // 에러!!! - var1은 constexpr 타입이므로 변경이 불가
}();
[var1]() mutable {
var1 = 999; // var1은 mutable로 선언되어 변경이 가능, 단 실제 값은 변하지 않음
std::cout << var1 << std::endl; // 999
}();
std::cout << var1 << std::endl; // 0
(2) 인자(Parameter)
- 일반 함수의 인자처럼 람다 함수에서 사용할 인자를 지정한다.
[](int a) -> int { return a; } // 1개의 인자를 설정
[](int a, int b) -> int { return (a+b); } // 2개의 인자를 설정
(3) 반환형(Return-Type)
- 람다 함수의 실행 결과를 반환할 반환 타입을 지정한다.
(4) 바디(Body)
- 람다 함수에서 수행할 작업을 기술한다.
2. 사용 예제
#include <iostream>
#include <functional>
int main()
{
// 간단한 람다 함수
[]() {
std::cout << "Simple Lamda Function" << std::endl;
}();
// 인자 2개를 더해서 sum에 저장하고, 더한 값을 반환하는 람다 함수
int sum{0};
// std::function 에 람다 함수를 정의
std::function<int(int,int)> fSum{[&sum](int a, int b) -> int {
sum = a + b;
return sum;
}};
std::cout << fSum(10, 20) << std::endl; // 30
std::cout << sum << std::endl; // 30
return 0;
}
3. 사용 예제 (클래스에서 사용)
#include <iostream>
#include <functional>
#include <string>
class Person
{
public:
Person(std::string&& pName, int pAge, std::function<std::string(std::string&, int)>&& pFunc)
: mName(std::move(pName))
, mAge(pAge)
, mTagFunc(std::move(pFunc)) // 람다 함수로 전달받은 임시 함수(r-value)를 std::move를 사용해 저장
{
// 출력 람다 함수에서 클래스의 멤버 변수를 사용하기 위해 this를 캡처한다.
mPrintFunc = [this]() {
std::cout << "Name: " << mName << ", Age: " << mAge
<< ", Tag: " << mTag << std::endl;
};
}
void executeTagFunc()
{
// 태그 생성 람다 함수 호출
mTag = mTagFunc(mName, mAge);
}
void executePrintFunc()
{
// Person 정보 출력 람다 함수 호출
mPrintFunc();
}
private:
std::function<std::string(std::string&, int)> mTagFunc{};
std::function<void()> mPrintFunc{};
std::string mName{};
std::string mTag{};
int mAge{0};
};
int main()
{
// 객체 생성시 [이름], [나이], [태그 생성 함수]를 인자로 받음
Person ps("Simson", 44, [](std::string& pName, int pAge) -> std::string {
return std::string(pName + "-" + std::to_string(pAge));
});
ps.executeTagFunc();
ps.executePrintFunc();
return 0;
}
#include <iostream>
#include <string>
using Type = std::string;
int main()
{
Type* str{new Type("Hello world !!!")};
std::cout << *str << std::endl;
delete str; // 동적 메모리 사용 후 삭제 필요
return 0;
}
3. std::unique_ptr
- 소유권을 하나만 가질 수 있는 포인터이다.
- 객체를 선언하여 사용한 후 사용 범위가 종료될 때 소멸자가 호출되어 메모리를 자동으로 삭제한다.
- 소유권을 이전하기 위해서는 std::move()를 사용해야거나 참조로 전달해야 한다.
- C++14 이후에는 std::make_unique() 함수를 통해 포인터를 생성할 수 있다.
#include <iostream>
#include <string>
#include <memory>
using Type = std::string;
void print(std::unique_ptr<Type>& pStr4)
{
std::unique_ptr<Type> pStr5{std::move(pStr4)};
std::cout << pStr4 << std::endl; // 0
std::cout << pStr5 << std::endl; // 0x141fc20
// 함수가 종료되면 pStr5는 삭제됨.
}
int main()
{
std::unique_ptr<Type> pStr{new Type("Hello world !!!")};
//std::unique_ptr<Type> pStr{std::make_unique<Type>("Hello world !!!")}; // C++14 이후
std::cout << pStr << std::endl; // 0x141fc20
Type* ppStr{pStr.get()}; // 주소값 가져오기
std::cout << ppStr << std::endl; // 0x141fc20
//std::unique_ptr<Type> pStr2{pStr}; // 컴파일 에러!! (소유권은 하나만 가능)
std::unique_ptr<Type> pStr3{std::move(pStr)}; // 소유권 이전 가능
std::cout << pStr << std::endl; // 0
std::cout << pStr3 << std::endl; // 0x141fc20
print(pStr3); // 인자로 넘겨서 참조로 받기 가능
std::cout << pStr3 << std::endl; // 0
// 동적 메모리 사용 후 삭제가 필요하지 않음.
return 0;
}
4. std::shared_ptr
- 소유권을 여러개 가질 수 있는 포인터이다.
- 소유자 수의 관리는 내부적으로 참조 카운터(Reference Counter)를 통해 이루어진다.
- 참조 카운터가 0이 되면 메모리를 자동으로 삭제한다.
- C++14 이후에는 std::make_shared() 함수를 통해 포인터를 생성할 수 있다.