moonshot.el 최초 릴리즈

Posted on Dec 29, 2019

moonshot.el 을 만들어서 공개했다.

이맥스를 써오면서 불편하다고 생각한걸 간단히 하고 싶었었다:

불편함들

빌드 디렉토리

Projectile을 사용하니까 프로젝트 디렉토리는 관리가 쉬운데, 빌드 디렉토리는 관리가 어려웠다.

  1. 아예 Projectile이 추론하는 디렉토리 바깥에 빌드 디렉토리는 위치하는 경우도 있으니까.

    • 예) GNU Autotools configure 이나 CMake으로 작업할 때, 소스트리 바깥에 빌드 디렉토리를 걸어놓아서 정리를 깔끔하게 할 수 있으니까.
  2. 그런데 이 '빌드 디렉토리'가 중요한게,

    • 말그대로 컴파일한 .o 파일이나 최종 결과 실행파일 등이 여기에 쌓이니까
    • 디버깅, 테스트를 위해서 실행파일을 실행하거나,
    • 심지어 make 이나 ninja 같은 빌드 커맨드를 실행하기 위해서도 빌드 디렉토리를 지정하거나 이동해서 실행해야 하니까 귀찮다.

compilation-mode, realgud: 실행 파일 찾아서 실행하기, 디버깅하기

예전에 만들어 써오던 이맥스를 위한 C/C++ 설정 처럼,

  1. 빌드 디렉토리를 적당히 감지해서,
  2. 현재 편집 중인 버퍼의 파일 이름과 Levenshtein 거리가 유사한 실행 파일을 우선으로 정렬한, 빌드 디렉토리에 속한 실행 파일 목록을 나열하고,
  3. 선택한 실행파일을 compilation-mode으로 실행하거나 realgud으로 디버깅 시작.

compilation-mode은 실행을 종료하고도 편집/빌드한 다음에 다시 compile-buffer에 가서 g 키만 누르면 다시 반복 실행하기 편안하니까 애용.

그런데, 이것도 꼭 C/C++ 개발할 때만이 아니라, 다른 언어를 사용할 때도, 예를 들어, 파이썬 같은 언어로 작성하고 chmod +x 해서 실행파일으로 만들어서 테스트 할 때도 편리하겠다고 생각했다.

이렇게 프로그래밍언어 mode별 설정을 만드는게 아니라, 그냥 유닉스에서 실행 가능한 파일을 쉘에서 실행하듯이 바로 선택하기 쉽게 고르고 실행할 수 있다면 편하겠다고 생각.

또 compilation-mode: configure + build을 위한 쉘 실행

위에 불편함들과 마찬가지로,

  1. 프로젝트 루트 에서 configure, cmake 을 실행하고, 그 결과가 빌드 디렉토리 에 쌓이도록 하고,
  2. 빌드 디렉토리 에 이동해서, 또 빌드 명령, make, ninja 등을 실행하는게,

생각보다 쉽게 자동화되거나 일반화되지 않아서, 귀찮았었다.

그래서 moonshot.el을 만들었다

프로젝트별 설정: .dir-locals.el

.dir-locals.el 은 조금 생소할 수도 있는데, 프로젝트 루트 디렉토리에 위치해놓고, 그 이하의 모든 파일을 열 때 자동으로 적용할 변수들을 지정할 수 있다.

프로젝트별 빌드 디렉토리의 설정 혹은 자동 유추

기본적으로는 projectile-project-root 을 쓰거나 현재 버퍼의 디렉토리를 빌드디렉토리로 쓴다. (가능하면 projectile을 이용)

그런데, 다른 빌드 디렉토리를 지정할 수 있도록, moonshot:project-build-dir 변수를 만들었다.

.dir-locals.el 파일에 설정해서, 프로젝트별로 특정한 디렉토리를 빌드디렉토리로 지정할 수 있도록 했다:

  1. 그냥 상대 경로를 문자열으로 설정하면, projectile의 루트나 현재 버퍼의 디렉토리에서 상대경로로 설정,
  2. 절대 경로면 그냥 그 경로가 빌드 디렉토리.
  3. 그것도 아니고, 코드를 실행해야 한다면, Lisp code을 그대로 적어서 빌드디렉토리를 찾아낼 수도 있도록 했다.

빌드 디렉토리에서 실행파일 찾아 실행/디버깅

moonshot:run-executable, moonshot:run-debugger

빌드 디렉토리를 지정할 수 있으니, 그냥 쉽게 실행파일을 찾아서 실행/디버깅 가능해졌다.

configure and make: 빌드 디렉토리에서

moonshot:run-runners 으로,

  1. 미리 preset으로 지정해놓은 명령을 실행,
  2. 프로젝트별로 추가 명령들을 dir-locals에 등록해 쓸 수 있다.

그리고, 명령에 포함되는 경로명 등을 필요에 맞게 쓸 수 있도록, %a 이면 현재 파일의 전체 경로, %p 이면 프로젝트 루트, %b 이면 빌드 디렉토리 등등의 경로 확장 기능을 만들었다.

이렇게만 해도, 명령을 일반화 해서 계속 한 프로젝트 안에서는 물론이고 다른 프로젝트에서도 그대로 사용하기 좋으니까.

예시: Python + Virtualenv을 쓸 때 스크립트를 실행하기/디버깅하기

에를 들어, 파이썬을 Virtualenv을 사용해서 작업하고 있고, Virtualenv 디렉토리를 moonshot:project-build-dir 으로 설정해 빌드 디렉토리로 지정했다.

  ;;; .dir-locals.el, 프로젝트 루트 디렉토리에 위치함.
  ((nil . ((*project-build-dir* . "venv")))) ; 프로젝트 루트 디렉토리 바로 밑에,
                                             ; `venv/' 디렉토리에 virtualenv == 빌드 디렉토리.

이제, 내가 작성한 파이썬 스크립트를 실행하려면, 다음을 거쳐야겠지.

  1. 빌드 디렉토리(venv/)에 위치한 Virtualenv을 source .../bin/activate 으로 활성화하고,
  2. 그 환경 안에서 내 파이썬 스크립트를 실행.

이렇게 하려면, 다음처럼 M-x moonshot:run-runners 을 실행하고 입력한다:

  source "%b/bin/activate"; cd "%p"; "%a"

%b 은 빌드 디렉토리 == Virtualenv 디렉토리.

%p 은 프로젝트 루트 디렉토리.

%a 은 현재 버퍼의 파일의 전체 경로.

결국, virtualenv을 activate하고, 프로젝트 루트 디렉토리로 이동해서, 스크립트를 실행.

이걸 매번 입력하려면 귀찮으니까, 설정으로 만들어 놓아도 좋다:

  (add-to-list 'moonshot:runners "source \"%b/bin/activate\"; cd \"%p\"; \"%a\"  # Run with Virtualenv")

마무리

완벽하게 모든 케이스를 다 커버하지는 못할지도 모르겠다. 하지만 조금 문서와 코드를 읽고, 이해를 하고, 여기저기 다른 언어나 프레임웍의 프로젝트에 아주 쉽게 적용하기 좋은 범용적인 도구일거 같다.

EmacsLisp을 작성하고, Ivy을 이용해서 대화 프롬프트 / 선택 화면 / 추천을 구현하고, compilation-mode을 활용해서 편안하게 개발사이클에서 반복적으로 행하는 작업을 쉽게 시작하고 반복하기 좋게 만드는게 편안할거 같다.

별거 아닌 빌드 커먼대를 입력하고 실행하는거지만, 이런걸 줄이고 매번 이맥스 설정을 특정한 프로젝트 구조에 맞춰서, 혹은 예상해서 짜놓는 것도 한계가 있다는 생각을 자주했는데, 차라리 이런 범용적으로 활용하기 좋은 구조를 만들어서 앞으로 더 편안하게 프로젝트를 시작하고 작업할 수 있을거 같다.