흐르는 시간의 블로그...

C++11과 C++14를 사용하기 위해 GCC 5.4.0 버전을 인스톨 했었다.

현재 6.2버전까지 있는 것으로 보인다.


https://ftp.gnu.org/gnu/gcc/ - GCC Source


다시 하려니 기억이 나질 않는다. ㅡㅡ;


그래서 과정을 하나씩 정리해보려 한다.


1. 소스 다운로드

[root@ordweb install]# wget https://ftp.gnu.org/gnu/gcc/gcc-5.4.0/gcc-5.4.0.tar.gz


2. 압축 해제

[root@ordweb install]# tar xvzpf gcc-5.4.0.tar.gz


3. 선행 요구사항 다운로드

[root@ordweb install]# cd gcc-5.4.0/

[root@ordweb gcc-5.4.0]# ./contrib/download_prerequisites


4. 컴파일 - 약 한시간 이상 걸린다. ㅠ.ㅠ

[root@ordweb gcc-5.4.0]# ./configure --prefix=/usr/local/gcc5.4 --enable-checking=release --enable-languages=c,c++


... 에러 발생 ...

...32bit lib가 없어서 발생...


[root@ordweb gcc-5.4.0]# ./configure --prefix=/usr/local/gcc5.4 --enable-checking=release --disable-multilib --enable-languages=c,c++

[root@ordweb gcc-5.4.0]# make

[root@ordweb gcc-5.4.0]# make install





관련해서... 또 다른 삽질...

내가 작성할 application이 실행할따 기존 버전의 gcc library 참조. ㅠ.ㅠ

gcc5.4의 lib64를 먼저 참조하도록 LD_LIBRARY_PATH 변경


[ub1st@ordweb ~]$ vi ~/.bash_profile

...

LD_LIBRARY_PATH=/usr/local/gcc5.4/lib64/:/usr/local/lib:/usr/lib:$LD_LIBRARY_PATH:$ORACLE_HOME/lib

export LD_LIBRARY_PATH

...

[ub1st@ordweb ~]$ source ~/.bash_profile




POCO Library를 gcc 5.4.0으로 컴파일 하기

  •  결국 다시 4.8 버전으로 컴파일해서 올림. ㅠ.ㅠ(앞의 글에서 적었던 문제)

[root@ordweb poco-1.7.5]# cd build/config

[root@ordweb poco-1.7.5]# vi Linux


CROSS_COMPILE = /usr/local/gcc5.4/bin/ 

CC      = ${CROSS_COMPILE}gcc

CXX     = ${CROSS_COMPILE}g++

...
#STRIP   = ${CROSS_COMPILE}strip
STRIP   = strip



perl에 CPAN 설치하고 cpan 콘솔에서 Archive:Zip 설치하려고 삽질하다 실패하고...

걍 git에서 다운받아서 설치.. (아... 눈물난다. ㅠ.ㅠ)


[root@ordweb install]# git clone https://github.com/redhotpenguin/perl-Archive-Zip.git

[root@ordweb install]# cd perl-Archive-Zip/

[root@ordweb perl-Archive-Zip]# perl Makefile.PL

[root@ordweb perl-Archive-Zip]# make

[root@ordweb perl-Archive-Zip]# make test

[root@ordweb perl-Archive-Zip]# make install





관련해서... 오라클의 인코딩이 이상해... ㅠ.ㅠ

한글을 제대로 못 읽는다!


[ub1st@ordweb ~]$ vi ~/.bash_profile

...

NLS_LANG=KOREAN_KOREA.KO16MSWIN949

export NLS_LANG

...

[ub1st@ordweb ~]$ source ~/.bash_profile





마지막 삽질...

코딩중에 file을 열고/쓰고/닫기 위해 기존 C언어의 open/write/close 를 사용...

나름 좀 좋게 해보겟다고 C++의  ofstream 사용... 

그러나 ofstream에서 UTF8 Write는 또 다른 문제 야기...

다시 C언어의 함수로 돌아옴... ㅠ.ㅠ


바로 직전의 poco library의 링크 문제와 GCC5.x.x와 연관된 부분이다.

poco library를 겨우겨우 컴파일해서 넘어갔는데 이번에는 OCCI에서 문제가 다시 발생했다.


POCO Library link 이슈 - GCC 5.4.0 업버전 후 생긴 문제



이번 개발에서는 오라클과 redis를 사용한다. poco library이후 occi를 위해 두개의 패키지를 인수톨 했다.

기존에 올렸던 글을 참고해도 된다.


instant client 설치하기



설치가 완료되었고 소스에 occi 클래스를 컴파일 했다.

ubiocci.cpp:(.text+0x197e): undefined reference to `oracle::occi::Number::fromText(oracle::occi::Environment const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
ubiocci.o: In function `CUbiOcci::AddParam(unsigned long long)':


뚜둥... 어제와 같은일이 또 발생했다.

대충봐도. std::__cxx11::bast_string<...> 부분이 보인다.

문제를 해결하다가 멈췄던 그 지점으로 다시 돌아가본다.

오늘은 그래도 좀 사용자가 있는 오라클 이슈... 나름 답이 쉽게 나온다.

OCCI linkage error with gcc 5

poco library 문제를 검색했을때도 나왔던 그 문구가 다시 나온다.
-D_GLIBCXX_USE_CXX11_ABI=0


define과 관련된 내용은.. 이리로 넘어간다.

Dual ABI

Application binary interface


다 찾아서 읽어보고 이해하라고 하면 나도 안하기 때문에...

나름 이해한바를 적어보면 아래와 같다.

SW분야에서 Application binary interface는 OS나 library간의 기계어 코드레벨의 인터페이스이다. ABI는 함수가 어떻게 호출되는지 하나의 프로그램 컴포넌트에서 다른 쪽으로 binary format information이 넘어가는지를 혹은 시스템 콜에 어떻게 반응하는지를 걸정한다. ABI는 보통 컴파일러, OS나 라이브러리 제작자 혹은 여러 프로그래밍 언어를 섞어서 다른 언어의 함수를 호출하는 것을 개발하는 개발자의 몫이다. API와 유사하지만 API는 소스 코드 레벨에서 이루어지고 ABI는 프로그램 컴포넌트간의 인터페이스이다.


ABI는 아래와 같은 상세한 것들을 포함한다.(해석하기 뭣해서 걍 위키피디아 붙임)

  • the sizes, layout, and alignment of data types
  • the calling convention, which controls how functions' arguments are passed and return values retrieved; for example, whether all parameters are passed on the stack or some are passed in registers, which registers are used for which function parameters, and whether the first function parameter passed on the stack is pushed first or last onto the stack
  • how an application should make system calls to the operating system and, if the ABI specifies direct system calls rather than procedure calls to system call stubs, the system call numbers
  • and in the case of a complete operating system ABI, the binary format of object files, program libraries and so on


GCC 5.1릴리즈는 std::string과 std::list의 새로운 구현을 포함한 새로운 라이브러리 ABI를 소개했다. C++11의 표준에 따른 작업이었다. 이미 존재하는 라이브러리에 대한 역호환성을 위해 ABI를 선택할 수 있도록 했다. 이것은 링크에서 다른 이름을 가지도록 인라인으로 define함으로써 가능하다. 예를 들어 새버전의 std::lst<int>는 실제로 std::__cxx11:list<int>로 정의한다.


_GLIBCXX_USE_CXX11_ABI 매크로는 라이브러리 헤더의 어떤 ABI를 선택할지 조정할 수 있다. GCC의 기본값은 1로 새로운 ABI를 사용하는 것이다. 기존의 라이브러리를 사용할 것이라면 어떤 라이브러리 헤더를 include 하기전에 매크로를 0으로 해야 한다. -std옵션은 ABI를 선택하지 않는다. 그래서 C++03과 C++11 코드를 함께 링크 할 수 있다.


컴파일시 -Wabi-tag 옵션을 통해 알 수 있다.


위의   OCCI linkage error with gcc 5  에서 나오듯이 일부 라이브러리만 이 옵션을 적용하게 할 방법은 없어 보인다. 모든 라이브러리는 동일한 ABI로 컴파일되어야 함께 동작이 가능하다. 

따라서 occi의 소스가 나에게 없으므로 결국 poco 라이브러리를 4.x.x gcc로 컴파일한 이후 -D_GLIBCXX_USE_CXX11_ABI=0를 통해 개발을 진행할 것이다.


       


현재 운영중인 시스템에서 poco library의 log시스템을 사용한다.

c++14를 사용하기 위해 최근 GCC를 5.4버전으로 올렸다.

그 과정의 수많은 삽질이 있었다. Linux를 잘아는 천과장이 아니었으면 지금도 미궁을 헤메고 있었을듯... :(


우쨋거나 GCC 5.4 업버전 이후 또 다른 문제에 봉착했다.

poco library가 정상 작동을 하지 않았다.

혹시 하는 생각에 poco-1.7.4로 업버전 하여 컴파일하여 install 하였다.

여전히 안된다.


configure를 살펴봐도 gcc 관련 내용은 없다.

여기저기 뒤져보다가 make file을 살펴보니 다름과 같은 라인이 보인다.


include $(POCO_BASE)/build/config/$(POCO_CONFIG)


시스템상 관련 파일은 ./build/config/Linux 이다

해당 부분에서 현재의 gcc5.4의 관련 부분으로 수정하여 컴파일 했더니 문제 없이 넘어가게 되었다.


#
# $Id: //poco/1.4/build/config/Linux#2 $
#
# Linux
#
# Make settings for Linux 2.6/gcc 3.3
#
#

#
# General Settings
#
LINKMODE ?= SHARED

#
# Define Tools
#
#CC      = ${CROSS_COMPILE}gcc
#CXX     = ${CROSS_COMPILE}g++
CC      = /opt/rh/gcc-5.4.0/bin/gcc
CXX     = /opt/rh/gcc-5.4.0/bin/g++


원래 나던 링크 에러 문제...


ubirloader.cpp:(.text+0x20a): 
undefined reference to `Poco::FileChannel::setProperty(std::__cxx11::basic_string<char, std::char_traits<char>, 
std::allocator<char> >  std::char_traits<char>, std::allocator<char> > const&)'
collect2: error: ld returned 1 exit status


일전에 pcqueue를 간단히 만들었었다.
창피해서 코드를 올리지 말까 하다가 올려본다.
실제 사용중에 있다.
허접한 코드를 개선해서 사용해도 좋다.
요기 출처를 달아주고 개선된 부분이 있으면 공유해 주면 더 바랄게 없다.
허접하다고 너무 심하게 구박하지 말아줬으면 좋겠다.

아래는 해당 pcqueue의 템플릿 코드이다.
실제 호출은 템픗릿 소스의 아래에 올려 놓겠다.

//---------------------------------------------------------------------------------------------------------------------
// pcqueue.hpp
//
// Asker's product consumer queue
//---------------------------------------------------------------------------------------------------------------------
#pragma once

#include <iostream>
#include <vector>
#include <pthread.h>

#include "errno.h"

#include "common.hpp"

using namespace std;
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
template <class T>
class CPCQueue
{
private:
	pthread_mutex_t ProducerLock;
	pthread_mutex_t ConsumerLock;

	vector<T*> ProducerQueue;
	vector<T*> ConsumerQueue;

private:
	bool LockProducer()
	{
		int _nError = pthread_mutex_lock( &ProducerLock );
		if ( _nError )
		{
			cout << "error in lock producer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in lock producer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}
	
		return true;
	}

	bool UnlockProducer()
	{
		int _nError = pthread_mutex_unlock( &ProducerLock );
		if ( _nError )
		{
			cout << "error in unlock producer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in unlock producer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}
	
		return true;
	}

	bool LockConsumer()
	{
		int _nError = pthread_mutex_lock( &ConsumerLock );
		if ( _nError )
		{
			cout << "error in lock consumer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in lock consumer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}

		return true;
	}

	bool UnlockConsumer()
	{
		int _nError = pthread_mutex_unlock( &ConsumerLock );
		if ( _nError )
		{
			cout << "error in unlock consumer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in unlock consumer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}

		return true;
	}


public:
	CPCQueue(){}
	virtual ~CPCQueue()
	{
		for ( class vector<T*>::iterator _iter = ProducerQueue.begin(); _iter != ProducerQueue.end(); _iter++)
			delete *_iter;

		for ( class vector<T*>::iterator _iter = ConsumerQueue.begin(); _iter != ConsumerQueue.end(); _iter++)
			delete *_iter;

		ProducerQueue.clear();
		ConsumerQueue.clear();
	}

	bool Init()
	{
		int _ErrorNo = pthread_mutex_init( &ProducerLock, NULL);
		if (_ErrorNo)
			return false;
	
		_ErrorNo = pthread_mutex_init( &ConsumerLock, NULL);
		if (_ErrorNo)
			return false;
	
		return true;
	}

	// Product에 하나를 추가한다
	bool AddProduct( T* _Job )
	{
		if ( false == LockProducer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		ProducerQueue.push_back(_Job);
	 
		if ( false == UnlockProducer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		return true;
	}

	// Consumer 쪽에서 하나씩 빼낸다.
	bool PopFromConsumer(T** _Value)
	{
		*_Value = NULL;

		if ( false == LockConsumer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		if (!ConsumerQueue.empty())
		{
			*_Value = ConsumerQueue.front();
			ConsumerQueue.erase(ConsumerQueue.begin());
		}

		if ( false == UnlockConsumer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		return true;
	}

	// 합할 것이 하나도 없는 경우 false를 반환한다
	bool MoveProducerToConsumer(int& _MoveCount)
	{
		if ( false == LockConsumer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		if ( false == LockProducer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		_MoveCount = ProducerQueue.size();
		if (!ProducerQueue.empty())
		{
			for ( class vector<T*>::const_iterator _iter = ProducerQueue.begin(); _iter != ProducerQueue.end(); _iter++)
				ConsumerQueue.push_back(*_iter);
	
			ProducerQueue.clear();
		}

		if ( false == UnlockProducer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		if ( false == UnlockConsumer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		return true;
	}

	int GetConsumerSize() const
	{
		return ConsumerQueue.size();
	}

};
//---------------------------------------------------------------------------------------------------------------------



class CDataThread: public CUbiThread
{
private:
	CPCQueue<COnePacket> PCQueue;
	int ThreadIndex;

	CURL* ctx;

public:
	bool Terminated;

public:
	CDataThread();
	virtual ~CDataThread();

	bool init();
	void SetIndex(int _Index);
	bool AddProduct( COnePacket* _OnePacket);

	virtual void Run();

private:
	bool DoPacketProcess(COnePacket* _OnePacket);
	CUbiParser* MakeParser(COnePacket* _OnePacket);
}

//---------------------------------------------------------------------------------------------------------------------
CDataThread::CDataThread()
{
}
//---------------------------------------------------------------------------------------------------------------------
CDataThread::~CDataThread()
{
}
//---------------------------------------------------------------------------------------------------------------------
bool CDataThread::init()
{
	return PCQueue.Init();
}
//---------------------------------------------------------------------------------------------------------------------
void CDataThread::SetIndex(int _Index)
{
	ThreadIndex = _Index;
}
//---------------------------------------------------------------------------------------------------------------------
bool CDataThread::AddProduct( COnePacket* _OnePacket )
{
	return PCQueue.AddProduct(_OnePacket);
}
//---------------------------------------------------------------------------------------------------------------------
void CDataThread::Run()
{
	int _Count = 0;
	int _JobCount = 0;
	int _SleepMicro;
	COnePacket* _OnePacket = NULL;

	Terminated = false;
	while(!Terminated)
	{
		try
		{
			// Customer Queue에 Producer Queue의 내용을 가져오게 한다
			if (!PCQueue.MoveProducerToConsumer(_Count))
				formatlog( LOG_FATAL, "CDataThread::%s(%d) %10d Thread Error in Move PCQueue data", __func__, __LINE__, ThreadIndex); //, typeid(T).name());
			else
				formatlog( LOG_INFO, "CDataThread::%s(%d) %10d Thread %2d %3d remain.", __func__, __LINE__, ThreadIndex, _Count, PCQueue.GetConsumerSize());
		}
		catch(exception &_innerException)
		{
			formatlog( LOG_CRIT, "Exception on CDataThread::%s(%d). %2d Thread what(%s)", __func__, __LINE__, ThreadIndex, _innerException.what());
		}

		// Customer Queue를 비울때 까지 작업을 처리 한다
		_JobCount = 0;
		while(true)
		{
			_OnePacket = NULL;

			// 처리 간격에 일정 시간을 둔다
			if (!PCQueue.PopFromConsumer(&_OnePacket))
			{
				formatlog( LOG_FATAL, "CDataThread::%s(%d). %2d Thread Error in Get Data From PCQueue", __func__, __LINE__, ThreadIndex);
				break;
			}

			if ( NULL == _OnePacket)
				break;

			//1 실질적인 패킷 처리!!
			DoPacketProcess(_OnePacket);
			_JobCount++;

			delete _OnePacket;
		}

		usleep(500000 );
	}
}