2009년 3월 18일 수요일

emacs 에서 per-directory local variables 와 anything 으로 프로젝트들 빌드하기

현재 ede 사용을 검토중인데 지금 내가 쓰는 방법을 먼저 적어둔다. 꽤 오래전부터 써오던 코드라 위키 등에도 적어둔건데 정리할겸.

내가 원하는것
1. 여러개의 프로젝트들을 목록으로 관리
2. 목록중 하나를 선택하면 해당 프로젝트 루트디렉토리를 dired 모드로
3. 해당 프로젝트 아래의 소스를 편집중에 f7 을 누르면 적당한 방법으로 빌드

이게 전부.
1,2 는 anything 으로 해결했고 3 은 직접만든 함수에 추가로 예전에는 dirvars.el 을 쓰다가 emacs 자체기능으로 전환.
비슷한 일을 하는 코드들이 많지만 써보지는 않았다. 그냥 내손에 맞게 만들다 보니 이런 모양이 됐더라. 자동완성(별 필요성을 못느끼지만) 때문에 ede 를 보고 있는 중인데 ede 를 적용하면 anything 을 ede 로부터 뽑아내야 하는등 다소 변화가 있어야 해서 일단 내걸 정리해둔다.

먼저 f7 을 누르면 컴파일 되도록 하는것부터.
간단히 f7 눌리는 순간 로컬변수로부터 컴파일 커맨드 가져와서 그걸 돌리는걸로 끝. 그런데 내가 cmake 를 쓰고 아웃오브소스 빌드를 좋아하니 빌드디렉토리가 현재 버퍼의 디렉토리와 달라서 이 정보까지 구겨박아야 했다.
이때 이 로컬변수 이름을 yoonkn-build-option 이라고 하고( yoonkn 붙인건 elisp 이 네임스페이스가 없기 때문 헐. 달리 붙인 단어가 없더라) 여러 정보가 들어가야 하니 alist 로 구성했다.(구버전에서는 plist 형태였는데.. 왜바꿨는지는 기억안나네)

즉 yoonkn-build-option 의 모양은 이런 형태
(setq yoonkn-build-option '((build-directory . "/tmp/foo")
(build-command . "make "))
이정보를 가지고 f7 눌리면 /tmp/foo 에 가서 make  돌리도록 해보자.
(defun yoonkn-build ()
(interactive)
(if (and (boundp 'yoonkn-build-option)
(not (null yoonkn-build-option)))
(let ((pwd (substring (pwd) (length "Directory ")))
(wc (cdr (assq 'build-directory yoonkn-build-option)))
(compile-command (cdr (assq 'build-command yoonkn-build-option))))
(unwind-protect
(progn
(cd wc)
(call-interactively 'compile))
(cd pwd)))
(call-interactively 'compile)))
(global-set-key [f7] 'yoonkn-build
좀 드럽지만 어쨌건 돌아간다..
cmake 알기 전에 msvc 쓸때는 devenv 에 커맨드 라인주고 빌드하기도 했는데 지금은 그렇게 쓸 필요가 없지. 그리고 익스프레스버전은(지금은 모르겠고 2005때는) 커맨드라인 빌드가 잘 안됐던것으로 기억하고. 뭐 어쨌건 상용 msvc 를 쓰고있다면 cmake 없이 바로 커맨드라인으로 빌드가 가능하니 위 함수로 ms 쪽빌드도 가능하다.

음.. 그런데 생각해보니 걍 cd .. ; make 해도 될것 같기도?? 헐

yoonkn-build-option 을 디렉토리마다 다르게 주기
위처럼 때려박으면 여러 프로젝트를 동시에 쓰기가 어려우니 per-dir local var 를 써먹자. 이건 전에 적었으니 대강 넘어가고, 그냥 요런식으로 쓰면 된다.
(define-project-bindings 'foo
'((nil . ((yoonkn-build-option . ((build-directory . "/tmp/foo")
(build-command . "make ")))))))
(set-directory-project "~/scratch/foo" 'foo
나는 위 코드를 office-projects.el 또는 home-projects.el 등에 나눠 넣고 머신마다 다른 el 을 부르도록 해서 쓰고있다.

음. 추가.
per-dir 로컬변수 세팅이 아주 반복적이니 매크로를 만들었다.
(defmacro defproject/yoonkn (name root builddir buildcmd)
`(progn
(define-project-bindings ,name
'((nil . ((yoonkn-build-option . ((build-directory . ,builddir)
(build-command . ,buildcmd)))))))
(set-directory-project ,root ,name)))
매크로를 쓰면 위 너저분한 코드가 아래 한줄로
(defmacro/yoonkn 'foo "~/scratch/foo" "/tmp/foo" "make ")

anything 에 등록하기
프로젝트간에 이동을 쉽게 하기 위해서 anything 을 활용했다. 적당히 anything-source 로 등록하면 그만
;;;; Per-Directory Local Variables 하고 anything 을 이용해서 프로젝트 관리
;;; define-project-bindings, set-directory-project 로 적절히 프로젝트를
;;; 세팅해 두면 그걸 anything 이 읽어서 dired 를 띄울수 있도록 해줬다.
(defun yoonkn-projects-candidates ()
(reverse ; reverse 안해주면 set-directory-project 역순으로 나온다
(mapcar #'(lambda (x)
(symbol-name (cdr x)))
project-directory-alist)))

(defun yoonkn-projects-get-dir (name)
(let* ((name-symbol (intern name))
(dir (car (find name-symbol project-directory-alist :key 'cdr))))
dir))

(defun yoonkn-projects-dired (name)
(dired (yoonkn-projects-get-dir name)))

(defun yoonkn-projects-psvn (name)
(psvn-status (yoonkn-projects-get-dir name)))

(setq anything-sources-my-projects
'((name . "내 프로젝트들")
(candidates . yoonkn-projects-candidates)
(action . (("dired 실행하기" . yoonkn-projects-dired)
("psvn 실행하기" . yoonkn-projects-psvn))))

우왕ㅋ굳ㅋ
이제 프로젝트 굵직한게 추가되면 적당히 등록하고 anything 켜서 이동후 코딩하다가 f7 누르면 장땡. 로컬변수 잘 활용라면 ifdef 모드등도 제어가능하고..


















댓글 없음: