- 빌드 시스템
- TL;DR
- 소스코드 작성
- autoconf
- aclocal
- automake
- autoreconf
- 프로젝트 배포하기
- 생성한 프로젝트 빌드 및 설치 해보기
- 실행파일 생성!
- 요약
- References
빌드 시스템
프로젝트의 소스코드가 한두개가 아니라 수십 수만개가 되면 gcc 로 일일이 컴파일을하여 하나의 executable 실행파일로 생성하는것은 불가능하다. 이를 위해 make 빌드시스템이 고대에 도입되었다(It was originally created by Stuart Feldman in April 1976 at Bell Labs.). make는 Makefile이라는 파일을 이용해 소스코드를 컴파일하고 빌드한다. Makefile은 컴파일을 위한 쉘스크립트라고 보면 쉽다. 일반 쉘스크립트를 이용해 빌드환경을 만들수 도 있다. 그러나 보통 쉘스크립트는 빌드환경에 특화되어있지 않기 때문에 거의 사용하지 않는다. 참고로 쉘스크립트와 Makefile 스크립트의 다른점 중 하나는 Increamental Build인데, 기존에 빌드를 진행한적이 있다면, 그 뒤에는 make가 최근 수정된 소스코드만 컴파일하여 최종 executable에 링크하는 를 할 수 있다.
또한 소스코드의 파일이 방대한경우 그리고 빌드하는 환경이 유저마다 서로 다른경우, 또한 여러가지 기능들을 사용자가 추가/제거 하고 싶은경우 Makefile에서 모든것을 처리하는게 쉬운일은 아니다. 그래서 대부분의 UNIX 유틸리티의 소스코드 패키지에는 autotools 로 생성한 configure파일 그리고 Mafefile.am파일이 존재해서 사용자가 직접 본인의 UNIX 환경에서 Makefile을 생성하게끔 한다. GNU autotools는 이렇게 불편을 최소화하기 위해 소스코드 패키지를 여러 UNIX계열 시스템에 이식성(portable)가능하게 만들어주는 도구다. 다시말해 이식성 가능한 프로젝트 빌드를 가능하게 해주는 도구다.
make는 거의 무려 50년전에 만들어진 소프트웨어이기 때문에 현대의 빠른 빌드를 요구하는 환경에는 적합하지 않을수도 있다. 그래서 여러가지 빌드시스템이 만들어졌고 사용되고 있다.
- Cmake
- Ninja (Chromium, Andriod Soong빌드 등)
- Meson (Systemd 등)
- Cargo (Rust)
거의 대부부분의 UNIX 유틸리티는 빌드할때 아래와 같은 과정을 거치도록 만들어졌다. ./configure
로 옵션으로 전달받은 Feature를 추가/제거하고 현재 호스트의 운영체제와 환경등을 고려하여 Makefile
을 생성한다. make
로 실제 소스코드를 빌드하고. make install
로 사용자의 환경에 install한다(실행파일과 라이브러리등을 적절한 path에 배치한다). 가령 grep-3.7 의경우 이렇게 빌드한다.
$ ./configure
$ make
$ make install
UNIX 유틸리티중 하나를 찾아서 소스코드를 다운로드 받아보자(Grep-3.7 다운로드). 소스코드 압축을 풀면 configure 파일이 존재한다. 이는 Makefile을 생성하는 쉘스크립트이다. 나는 소스코드에서 configure 파일의 라인수를 보고 내 두 눈을 의심했다. 파일이 5만 라인이 넘었기 때문이다. 이걸 인간이 짤 수 있다는 말인가? 유틸리티 소스코드도 작성하기 힘든데 빌드시스템을 위해 이렇게 많은 스크립트를 작성해야 한다는 말인가? 하는 생각들에 아찔해졌다. 하지만 파일 상단 주석에 이렇게 쓰여있었다. Generated by GNU Autoconf 2.71 for GNU grep 3.7.
다행히도 configure 스크립트는 자동생성 되는것이다. 바로 autoconf 라는 프로그램이 자동으로 생성해준다. autoconf가 얼마나 많은 라인을 만들어내는지 간단한 autoconf 스크립트를 작성해 실험해보자.
먼저 디렉터리 하나를 생성하고 autoconf.ac 라는 파일을 생성하여 아래의 두 줄을 작성한다. autoconf.ac 이 configure 파일을 생성하기 위해 사용자가 작성해야할 파일이다. 자세한 내용은 아래에서 다시 설명할 것이다.
AC_INIT([helloworld], [0.1])
AC_OUTPUT
그리고 Shell에서 autoconf 프로그램을 실행한다. 그러면 아래와 같이 configure 파일이 생성된것을 알 수 있다.
$ aclocal
$ autoconf
$ ls
autom4te.cache configure configure.ac
그리고 configure파일을 열어서 확인해 보자. 고작 두줄짜리로 부터 얼마나 많은 라인의 configure스크립트가 자동생성되었는지… (나는 2402라인이 생성되었다.).
TL;DR
이제 autotools 를 이용해 간단한 helloworld 프로그램의 소스코드 패키지 레이아웃을 UNIX스럽고 프로페셔널 해보이게(?) 구성해보자. 결론부터 이야기하면 GNU autotools 에서 사용하는 도구는 다음과같이 두가지다.
- configure.ac 파일로 부터 configure 파일 생성 명령어
$ autoconf
- Makefile.am 파일로 부터 Makefile.in 생성 명령어
$ automake --add-missing
소스코드 작성
먼저 소스코드를 작성해보자. 이 예제에서는 간단하게 helloworld.c 하나를 작성했다.
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("hello, world!\n");
return 0;
}
autoconf
autoconf
는 configure 스크립트를 자동생성해주는 도구다. autotools 는 m4 매크로 패턴을 사용한다. 아래와 같이 매크로를 사용해 최소한의 정보를 입력해보자.
AC_INIT([helloworld], [0.1], [jihuun.k@gmail.com])
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
AC_INIT
autoconf
에게 생성할 프로그램 이름, 그리고 버전정보, 그리고 누가 maintainer인지 알려준다.AM_INIT_AUTOMAKE
automake도 사용할 예정이라면 추가한다.AC_PROG_CC
사용자의 현재 환경의 C 컴파일러를 사용하도록 설정한다.AC_CONFIG_FILES
AC_CONFIG_FILES([filename]) 은 filename.in 파일로부터 filename파일을 생성한다는 의미다.-
AC_OUTPUT
최종 output을 생성한다는 의미 configure.ac 스크립트의 마지막에 위치한다. - 보다 자세한 autoconf 매크로 정보는 GNU autoconf manual에서 확인가능하다. https://www.gnu.org/software/autoconf/manual/autoconf-2.66/autoconf.html
그 뒤에 아래와 같이 autoconf 명령을 수행하면 configure파일이 생성된다.
$ aclocal
$ autoconf
$ ls
autom4te.cache configure configure.ac
aclocal
한편 autoconf
명령을 사용하기 전에 aclocal
명령어를 통해 사전에 환경을 설정해줘야한다. aclocal 명령을 수행하면 aclocal.m4 파일이 생성된다. 그 뒤 autoconf 수행하면 configure 파일을 생성할 수 있다. 참고로 aclocal을 하지 않으면 아래와 같은 에러가 발생한다.
$ autoconf
configure.ac:2: error: possibly undefined macro: AM_INIT_AUTOMAKE
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.
$ aclocal
$ autoconf
automake
automake
는 Makefile.am 파일로부터 Makefile.in 파일을 생성하는 도구이다.
- Makefile.am 생성
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = helloworld
helloworld_SOURCES = helloworld.c
AUTOMAKE_OPTIONS = foreign
GNU 프로젝트의 Standard Layout을 따르지 않을거라면 추가한다.bin_PROGRAMS = programname
생성할 실행파일(프로그램) 이름을 기입한다.programname_SOURCES = srcfilename.c
소스코드 파일명을 기입한다.
이제 automake를 수행하면 Makefile.in
이 생성된다.
$ automake --add-missing
-
참고
–add-missing 사용하는 이유: automake 는 특정상황에 따라 여러가지 common파일들을 요구한다. 이 옵션을 사용하면 그파일들을 자동생성해준다. -
참고
AUTOMAKE_OPTIONS = foreign
이 없을때 발생하는 에러 GNU 프로젝트의 standard Layout 을 추가하지 않아서 에러가 발생한다. 아래와 같은 파일들이 생성되어야 한다.
$ automake --add-missing
configure.ac:3: installing './compile'
configure.ac:2: installing './install-sh'
configure.ac:2: installing './missing'
Makefile.am: installing './INSTALL'
Makefile.am: error: required file './NEWS' not found
Makefile.am: error: required file './README' not found
Makefile.am: error: required file './AUTHORS' not found
Makefile.am: error: required file './ChangeLog' not found
Makefile.am: installing './COPYING' using GNU General Public License v3 file
Makefile.am: Consider adding the COPYING file to the version control system
Makefile.am: for your code, to avoid questions about which license your project uses
Makefile.am: installing './depcomp'
- 최종 생성된 파일들
$ ls
aclocal.m4 autom4te.cache compile configure configure.ac COPYING depcomp helloworld.c INSTALL install-sh Makefile.am Makefile.in missing
autoreconf
참고로 autoreconf
를 사용하면 aclocal
, autoconf
및 automake
를 직접 하나하나 수행할 필요없다. 알아서 한번에 수행해준다.
$ autoreconf -i
프로젝트 배포하기
이제 드디어 configure
스크립트와 Makefile.in
파일이 우리 프로젝트에 생성되었다. 이제 소스코드를 다운로드 받은 사용자는 configure
와 make
를 사용해 프로젝트를 빌드 할 수 있다. 프로젝트는 이상태에서 배포가능하다. make dist
를 수행하면 배포가능한 tarball이 생성된다.
$ make dist
$ ls
또한 다음과 같이 배포할 tarball이 여러 환경과 조건에서 정상적으로 install될 수 있을지 테스트해볼 수 있다.
$ make distcheck
생성한 프로젝트 빌드 및 설치 해보기
이제 아래의 UNIX 빌드 명령으로 프로젝트 빌드를 해보자. configure 실행하여 Makefile을 생성하고 make를통해 빌드해보자.
$ ./configure
$ make
$ make install
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
$ make
CDPATH="${ZSH_VERSION+.}:" && cd . && /bin/sh /home/jihuun/project/c_language/autotools_test/missing autoconf
/bin/sh ./config.status --recheck
running CONFIG_SHELL=/bin/sh /bin/sh ./configure --no-create --no-recursion
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking that generated files are newer than configure... done
configure: creating ./config.status
/bin/sh ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
gcc -DPACKAGE_NAME=\"helloworld\" -DPACKAGE_TARNAME=\"helloworld\" -DPACKAGE_VERSION=\"0.1\" -DPACKAGE_STRING=\"helloworld\ 0.1\" -DPACKAGE_BUGREPORT=\"jihuun.k@gmail.com\" -DPACKAGE_URL=\"\" -DPACKAGE=\"helloworld\" -DVERSION=\"0.1\" -I. -g -O2 -MT helloworld.o -MD -MP -MF .deps/helloworld.Tpo -c -o helloworld.o helloworld.c
mv -f .deps/helloworld.Tpo .deps/helloworld.Po
gcc -g -O2 -o helloworld helloworld.o
실행파일 생성!
이제 helloworld 실행파일이 생성되었다.
$ ./helloworld
hello, world!
이제 make install
하면 현재 환경변수에 실행파일이 등록되어, 어느 위치에서든 helloworld 프로그램을 실행 시킬수 있다.
$ make install
$ helloworld
hello, world!
참고로 최종 생성된 파일은 다음과 같다. 오지게 많다.
$ ls
aclocal.m4 autom4te.cache compile config.log config.status configure configure.ac COPYING depcomp helloworld helloworld.c helloworld.o INSTALL install-sh Makefile Makefile.am Makefile.in missing
요약
위의 내용을 정리하면 다음과 같다.
- 프로젝트 생성자 관점
$ aclocal # m4 환경 생성
$ autoconf # configure.ac 파일로 부터 configure 스크립트 생성
$ automake --add-missing # Makefile.am 로부터 Makefile.in 생성
$ make dist # 배포용 tarball생성
$ make distcheck # 배포용 tarball 테스트
- 사용자 관점 빌드 해당 프로젝트 소스코드를 다운받고 본인의 환경에 설치하는 사람들은 다음의 절차를 수행할 것이다.
$ ./configure # Makefile.in로부터 Makefile생성
$ make # 전체 프로그램 빌드
$ make install # 프로그램 설치, 실행 파일 및 라이브러리 등을 배치
References
- https://en.wikipedia.org/wiki/GNU_Autotools
- https://www.gnu.org/software/autoconf/manual/autoconf-2.66/autoconf.html
- https://www.gnu.org/software/automake/manual/automake.html#Public-Macros
- https://thoughtbot.com/blog/the-magic-behind-configure-make-make-install
- https://www.geeksforgeeks.org/autoreconf-command-in-linux-with-examples/amp/
- https://youtu.be/4q_inV9M_us
- https://earthly.dev/blog/autoconf/
- https://stackoverflow.com/questions/27285052/difference-between-autoconf-and-autoreconf#:~:text=1%20Answer&text=autoconf%20generates%20the%20configure%20script,like%20aclocal%20%2C%20automake%20%2C%20etc.&text=You’ll%20usually%20just%20call,lower%20level%20tools%20