본문 바로가기
컴퓨터/자질구레 팁

Makefile 파일을 자동으로 만들기

by adnoctum 2011. 1. 19.


   의존성과 갱신된 파일을 고려하여 자동으로 컴파일을 해주는 작은 프로그램인 make 의 인자로 들어가는 Makefile 파일을 자동으로 만드는 스크립트를 만들어서 사용해 보자.

   make 는 인자가 없이 실행되면 그 명령어가 실행된 경로에 존재하는 Makefile 을 읽어서 그 안에 지시된 대로 컴파일 및 link 를 하는 유틸리티이다. 그런데, 나의 경우, 분석하는 cpp 파일을 수시로 새로 만들게 되고, 개인적으로 사용하는 library 도 빈도가 적기는 하나 새로 만들거나 수정이 된다. 그런데 이럴 때마다 Makefile 을 새로 만드는 것이 귀찮더군. 그래서 아예 Makefile을 자동으로 만드는 스크립트를 작성하여 사용한다. 그 스크립트 이름이 makefile.py 인데, 이 스크립트가 실행된 경로에 있는 모든 cpp 파일을 읽어서, 그 cpp 파일들이 사용하는 개인 library 와 별도의 외부 library 를 compile 하기 위한 지시자를 Makefile 에 작성하는 스크립트인 것이다. 한 번 만들어 놓으니 거의 수정 없이 여기저기서 사용하게 되었다. ㅋㅋㅋ. 역시나 진리의 파이썬으로 초간단하게 작성. 다음은 그 코드다. 당연히 저 코드를 그대로 사용할 수는 없을테지. 이 글은, 일단, 이렇게 할 수 있다는 것을 말함과 동시에, 그 예를 보여 주기 위한 글이다. (파이썬에선 줄 마지막에 세미콜론이 필요 없다는 것을 나도 알고 있다. 하지만 C++ 과 파이썬 코딩을 '동시에' 하는 일이 잦은 나는 차라리 둘 모두에서 ; 를 붙이는 것이 편하기 때문에 그냥 이렇게 하고 있다)

#! /usr/bin/python

import os;
import sys;
import re;
import glob;

def has_main(cpp_file):
    f = open(cpp_file);
    for line in f:
        if line.find('int main()' == 0: return True;
   
    return False;


def get_custom_header(cpp_file):
    header_path = '/home/adnoctum/LJSLibrary/headers/';
    f = open(cpp_file);
    header = [];
    for line in f:
        m = re.search('#include <(.*?)>$',line);
        if m:
            if os.path.exists(header_path+m.group(1)) == True:
                header.append(m.group(1));
    return header;
   
def get_all_included(cpp_file):
    f = open(cpp_file);
    header = [];
    for line in f:
        m = re.search('#include <(.*?)>$',line);
        if m:
            header.append(m.group(1));
    return header;
   

def post_process_executible_command(exec_cmd, cpp_file):
    all_included = get_all_included(cpp_file);
    if all_included.count('gmp.h') == 1:
        exec_cmd = exec_cmd + ' -lgmp';
    if all_included.count('gmpxx.h') == 1:
        exec_cmd = exec_cmd + ' -lgmpxx';

    return exec_cmd;

def main():
    all_cpp_files = glob.glob('*.cpp');
    content = [];
    custom_header = [];
    all = 'all:';
    for cpp_file in all_cpp_files:
        if has_main(cpp_file) == False: continue;
        executible = cpp_file[0:-4];
        obj = executible + '.o';
        all = all + ' ' + executible;
        header = get_custom_header(cpp_file);
        lib_config = '';
        for h in header:
            if lib_config.find(h[0:-1]) == -1:
                # statutil.o should be supplied with modutil.h because modutil.h uses statutil.h
                if h[0:-1] == 'modutil.':
                    if lib_config.find('statutil') == -1:
                        lib_config = lib_config + ' ' + '$(LIB_PATH)/' + 'statutil.o';
                        if custom_header.count('statutil.h') == 0: custom_header_append('statutil.h');
                lib_config = lib_config + ' ' + '$(LIB_PATH)/' + h[0:-1] + 'o';
                if custom_header.count(h) == 0: custom_header.append(h);
        lib_config = lib_config.strip();

        exec_req = executible + ': '+ obj + ' ' + lib_config;
        exec_cmd = '\tg++ -o ' + executible + ' ' + obj + ' ' + lib_config;

        exec_cmd = post_process_executible_command(exec_cmd, cpp_file);

        obj_req = obj + ': '+cpp_file;
        obj_cmd = '\tg++ -c '+cpp_file;

        content.append(exec_req);
        content.append(exec_cmd);
        content.append(obj_req);
        content.append(obj_cmd);
        content.append('\n');
   

    content.insert(0, all+'\n');

    for ch in custom_header:
        obj = ch[0:-1]+'o';
        obj_cpp = ch[0:-1]+'cpp';

        obj_req = '$(LIB_PATH)/'+obj + ': $(LIB_SRC_PATH)/'+obj_cpp;
        obj_cmd = '\tg++ -c $(LIB_SRC_PATH)/'+obj_cpp;
        obj_move = '\tmv '+obj+' $(LIB_PATH)';

        content.append(obj_req);
        content.append(obj_cmd);
        content.append(obj_move);

    w = open('Makefile', 'w');
    w.write('LIB_PATH=/home/adnoctum/LJSLibrary/library\n');
    w.write('LIB_SRC_PATH=/home/adnoctum/LJSLibrary/source\n');
    w.write('\n');

    for line in content:
        w.write(line+'\n');

    w.close();

    os.system('make');



if __name__ == '__main__':
    main();

요것이 makefile.py 파일. syntax highlight 가 없어서 보기 불편한데, 어쨌든, ㅋ. 역시나 진리의 파이썬!!