내 자리에서 빌드한 것은 되는데 빌드 서버에서 만든 것은 안 됐고 양쪽이 쓴 소스는 같다.
Table of contents
Open Table of contents
컴파일러가 달랐다
소스가 같은데 결과가 다르면 소스 밖에서 차이가 생긴 것이다. svn info 로 양쪽 리비전이 같은 것부터 확인하고 버전을 찍어 봤다.
$ gcc --version
gcc (GCC) 4.8.5 (내 자리)
gcc (GCC) 4.4.7 (빌드 서버)
4.4에는 없는 문법을 쓰고 있어서 내 자리에서는 통과하고 서버에서는 오류가 난다.
이번엔 오류가 나서 바로 알았는데 빌드가 실패하면 그 자리에서 원인을 찾게 된다. 오류가 안 나고 동작만 다른 경우가 더 무섭고 그런 것은 훨씬 늦게 드러난다.
무엇이 다른지 전부 봤다
문제가 컴파일러만이 아닐 수 있어서 빌드에 관여하는 것을 전부 찍어 봤다.
#!/bin/sh
echo "gcc: $(gcc --version | head -1)"
echo "make: $(make --version | head -1)"
echo "ld: $(ld --version | head -1)"
echo "libc: $(ldd --version | head -1)"
echo "kernel: $(uname -r)"
echo "arch: $(uname -m)"
두 곳에서 돌려 diff 로 비교했다.
내 자리 빌드 서버
gcc 4.8.5 4.4.7
make 3.82 3.81
libc 2.17 2.12
kernel 3.10 2.6.32
네 가지가 전부 다르다. 이번 문제의 원인은 컴파일러였지만 나머지도 언젠가 문제가 된다. 하나만 맞추고 끝냈으면 다른 것 때문에 또 같은 일을 겪었을 것이다.
어느 쪽에 맞출지 정했다
두 선택지가 있었다. 빌드 서버를 올리면 배포 대상 서버도 같이 올려야 하고 라이브러리 호환을 봐야 한다. 반대로 내 자리를 낮추면 개발이 불편해진다.
배포 대상 서버가 빌드 서버와 같은 판이었다. 거기서 도는 것이 목적이니 빌드 서버 쪽에 맞추기로 했다. 내 자리가 더 최신이라도 운영과 다르면 맞지 않는 것이다.
내 자리에 옛 버전을 따로 설치하고 그것으로 빌드하게 했다.
/opt/gcc-4.4.7/bin/gcc
CC ?= /opt/gcc-4.4.7/bin/gcc
기본은 옛 버전이고 필요하면 CC 환경 변수로 바꾼다.
빌드 환경 확인 단계
빌드를 시작할 때 컴파일러 버전을 확인하고 안 맞으면 그 자리에서 멈추게 했다.
check-env:
@v=$$($(CC) -dumpversion); \
if [ "$$v" != "4.4.7" ]; then \
echo "컴파일러 버전 불일치: $$v (기대: 4.4.7)"; \
echo "CC 를 확인하십시오. 현재: $(CC)"; \
exit 1; \
fi
@echo "빌드 환경 확인 완료"
all: check-env $(TARGET)
-dumpversion 으로 받은 값이 다르면 exit 1 이다. 빌드가 시작되기 전에 걸리므로 다 컴파일하고 나서 링크에서 실패하는 것보다 낫다.
검증 — 빌드 정보와 배포 자리
만들어진 실행 파일이 어느 환경에서 나온 것인지 알 수 있게도 했다.
static const char BUILD_INFO[] =
"built " __DATE__ " " __TIME__
" gcc " __VERSION__;
void printBuildInfo(void) {
printf("%s\n", BUILD_INFO);
}
__DATE__ 와 __TIME__ 은 전처리기가 컴파일 시각으로 바꿔 주는 표준 매크로라 따로 넣을 것이 없고 __VERSION__ 이 컴파일러 판을 준다.
$ ./app --version
built Apr 16 2016 21:42:11 gcc 4.4.7
문제가 났을 때 어느 빌드인지 물어보면 되고 이것이 없으면 언제 빌드한 것인지 물어도 답이 안 나온다.
각자 자리에서 빌드해서 올리는 방식 자체도 문제라 빌드 서버에서만 만들고 그 결과물을 배포하게 바꿨다. 개발 중에는 각자 빌드하되 배포용은 빌드 서버 것만 쓴다.
echo "$(date +%Y%m%d-%H%M) $(svnversion)" > build/RELEASE
빌드 서버에서 만든 것에 RELEASE 표시를 붙이고 배포 전에 이 파일이 있는지 확인한다. 없으면 개인 빌드다.
빌드 서버를 다시 만들 일이 있을 수 있으니 무엇이 깔려 있는지도 적었다.
빌드 서버 환경
OS CentOS 6.7
gcc 4.4.7 (기본 저장소)
make 3.81
추가 라이브러리
- libssl-devel 1.0.1e
- zlib-devel 1.2.3
버전을 적은 것이 중요했다. 최신이라고 적으면 다시 만들 때 다른 것이 깔린다.
정리
- 같은 소스라도 빌드 환경이 다르면 결과물이 다르다
svn info로 리비전이 같은 것부터 확인한다- 컴파일러만 보지 말고 빌드에 관여하는 것을 전부 찍어
diff로 비교한다 - 하나만 맞추면 다른 것 때문에 또 겪는다
- 빌드가 실패하면 오히려 다행이고 동작만 다른 것이 더 위험하다
- 어느 쪽에 맞출지는 배포 대상 환경을 기준으로 정한다
- 빌드 전에
-dumpversion으로 확인하고 안 맞으면exit 1로 멈춘다 __DATE__·__TIME__·__VERSION__을 결과물에 박아 둔다- 배포용은 한 자리에서만 만들고 그 결과물에 표시를 붙인다
- 빌드 환경을 버전까지 적어 문서로 남긴다