koodev

pip 설치 중 setuptools 관련 오류

Programming

tensorflow-datasets 이란 파이썬 패키지를 설치 중 아래와 같은 오류 메시지가 나왔다.

원인은 setuptools 가 만료되서 라고 한다. pip install --upgrade setuptools

pip install --upgrade setuptools

입력 후 다시 pip install tensorflow-datasets 하니까 잘 동작하는 것을 확인했다.

참고: https://steemit.com/kr-dev/@wanabe/6hhg8

'Programming' 카테고리의 다른 글

PyTorch torch_shm_manager Runtime Error  (0) 2019.10.23
Tentative Symbol in C  (0) 2019.06.07
ARM A32 명령어셋 VZIP  (0) 2019.03.11
ARM A64 명령어셋 ZIP1, ZIP2  (0) 2019.03.11
ARM A64 명령어세트 기본적인 내용 정리  (0) 2019.03.11

ARM A32 명령어셋 VZIP

Programming

A32 명령어셋의 VZIP에 대해서 알아보자. 12.130 VZIP 을 참고했다.

이 명령어는 A64 버전의 ZIP1/ZIP2와 마찬가지로 두 벡터의 상위/하위 절반을 각각 떼어다가 뒤섞는다.

A32 버전의 VZIP은 특이하게도 Destination 피연산자가 따로 없다. 두 개의 피연산자를 갖는데 둘 다 상태가 변한다.

VZIP{cond}.size Qd, Qm
VZIP{cond}.size Dd, Dm
  • cond: 옵션. 조건처리.
  • size: 8 또는 16 또는 32.
  • Qd, Qm: 벡터 SIMD/FP 레지스터. Quadword.
  • Dd, Dm: 벡터 SIMD/FP 레지스터. Doubleword.

뒤섞는 방법은 아래 그림과 같다. 말하자면 A64 버전의 ZIP1ZIP2를 합쳐 놓은 것.

ARM A64 명령어셋 ZIP1, ZIP2

Programming

우선 ZIP1부터 정리한다. 이 명령어는 두 벡터에서, 즉 두 개의 SIMD/FP 레지스터에서 상위 절반(upper half)부분을 각각 떼어다가 뒤섞어(interleave) 붙인다. 설명보다는 그림으로 이해하는 것이 편할 듯.

ZIP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

위 그림에도 나와있듯이 ZIP2는 하위 절반 비트를 뒤섞는다.

뒤섞는 단위는 T파라미터에서 명시해 주는데, 8B, 16B, 4H, 8H, 2S, 4S, 2D 가 가능하다.

ARM A64 명령어세트 기본적인 내용 정리

Programming

ARM 명령어를 난생 처음 작성하다가 잘 안되어 기본적인 내용을 정리해본다. 이 내용은 Structure of the ARM A64 instruction set 에서 가져왔다.

레지스터

A64 아키텍쳐는 30개의 정수 레지스터가 있으며 각각은 x0부터 x31까지의 이름을 갖는다.

여기에 스택포인터 레지스터와 제로 레지스터가 있다. 제로 레지스터는 모든 비트가 0으로 고정된 레지스터이다. 그런데 이들 두개 모두 31번 레지스터 번호로 인코딩된다. 그리고 인스트럭션에 따라 스택포인터로 쓸지 제로레지스터로 사용할지가 결정된다.

30번 레지스터인 x30은 리턴주소를 저장하는데 사용된다.

여기서 이야기하는 레지스터들은 모두 길이가 64비트이다. 이들의 하위 32비트에 접근하는 용도로 w0에서 w31을 사용할 수 있다. 하위 비트에 쓰기 명령을 수행하면 상위 비트는 남아있게 될까? 그렇지는 않다. 하위 32비트에 쓰기 명령을 내리면 상위 32비트가 클리어된다.

부동소수점/벡터 레지스터

A64에는 위의 정수 레지스터와는 별개로 부동소수점이나 128비트 벡터 연산을 위한 32개의 레지스터가 존재한다. 이들은 벡터 연산을 지원하기 때문에 한꺼번에 64비트나 128비트씩 동작하도록 할 수 있다. 즉, 한번에 8비트 혹은 16비트, 32비트, 64비트씩 나누어 쓸 수 있다. 이렇게 하면 한번에 여러개의 동일한 연산을 수행시킬 수 있어서 코드 실행속도를 높일 수 있다. 원래는 루프를 돌면서 해야 할 일을 한꺼번에 수행하는 것이다.

명령어

명령어들은 ARM ARM for ARMv8-A 문서에서 설명되어 있다. 명령어 갯수는 앨리어스를 제외하고 442개 정도 된다고 한다.

A64 명령어들은 모두 32비트 길이이다.

(명령어들의 레벨 분류 - main encoding, instruction group, decode group, instruction - 에 대해서는 이해하지 못해 정리할 수 없었다)

같은 이름을 쓰고 피연산자를 달리 갖는 명령어가 있다. 예를 들어 아래 FMUL명령어는 두 개의 부동소수점을 곱한다. C 언어에서 a = b * c 와 같은 것이다. A64에서는 피연산자에 따라 아래에서 보여지는 것들 중 하나가 될 것이다.

fmul s0, s1, s2            ;single precision floats
fmul d0, d1, d2            ;double precision floats
fmul v0.2s, v1.2s, v2.2s   ;vectors with two singles
fmul v0.4s, v1.4s, v2.4s   ;vectors with four singles
fmul v0.2d, v1.2d, v2.2d   ;vectors with two doubles
fmul s0, s1, v2.s[0]       ;multiplies by a vector element
fmul d0, d1, v2.d[0]
fmul v0.2s, v1.2s, v2.s[0] ;combinations of the above
fmul v0.4s, v1.4s, v2.s[0]
fmul v0.2d, v1.2d, v2.d[0]

이렇게 하나의 FMUL에서 많은 변형이 생기게 된다. 모든 대표 명령어에서 이렇게 변형이 많이 생기는 것은 아니다. 그래서 이런 변형까지 다 계산하면 모든 명령어의 수는 1000에서 2000개 정도가 된다고 한다. 명령어세트 설계자는 이들 변형과 피연산자까지 모두 포함하여 32비트 길이의 공간에 인코딩해야 하며 혹시라도 나중에 추가될 수도 있는 부분을 위한 여분까지 남겨두어야 할 것이다.


나머지 부분은 이해를 못해서 제외한다.

'Programming' 카테고리의 다른 글

ARM A32 명령어셋 VZIP  (0) 2019.03.11
ARM A64 명령어셋 ZIP1, ZIP2  (0) 2019.03.11
Bash에서 문자열 검색 조건식 만들기  (0) 2018.07.22
Truncate PWD in prompt (MacOS)  (0) 2018.04.29
Regular expression in sed  (0) 2018.04.25

Bash에서 문자열 검색 조건식 만들기

Programming

이전에 다른곳에 썼던 내용을 이 블로그에 기록함.

안드로이드 빌드 스크립트를 만들다가 PATH 변수에 특정 문자열이 없을 경우 이를 추가해주는 기능이 필요했다. 관련 내용을 구글링해보니 대부분 문자열 치환에 대해서만 다루고 있었는데, stackoverflow 에서 마침내 원하는 답을 찾을 수 있었다.

http://stackoverflow.com/questions/229551/string-contains-in-bash

string = 'My long string';
if [[$string == *"My long"*]]
then
    echo "It's there!";
fi

이런식으로 대괄호 두개와([[, ]]) 와일드카드 캐릭터(*)를 써서 패턴 검사가 가능하다. 부정 연산자를 넣기 위해 좀 삽질을 했는데, 아래와 같이 ==을 !=으로 바꾸어주면 된다.

JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
if [[$PATH != *"$JAVA_HOME"*]]; then
    export PATH=$PATH:$JAVA_HOME/bin
fi

'Programming' 카테고리의 다른 글

ARM A64 명령어셋 ZIP1, ZIP2  (0) 2019.03.11
ARM A64 명령어세트 기본적인 내용 정리  (0) 2019.03.11
Truncate PWD in prompt (MacOS)  (0) 2018.04.29
Regular expression in sed  (0) 2018.04.25
How to terminate a background process  (0) 2018.04.24

Truncate PWD in prompt (MacOS)

Programming

쉘 프롬프트에 현재 디렉토리 위치를 넣을 경우, 디렉토리 깊이가 깊어질수록 터미널이 지저분해지게 된다. Ubuntu 등의 배포판에서는 PROMPT_DIRTRIM 변수에 숫자를 지정해주면 그 숫자만큼만 프롬프트상에 표시되는 현재 디렉토리 경로를 줄여준다. 그렇지만 MacOS 기본 Bash 쉘(3.2.57)에는 PROMPT_DIRTRIM 이 구현되어 있지 않다. Bash 자체 버전을 올려도 되지만, 순정 상태를 건드리고 싶지는 않다(...). 그런데 손으로 구현하는 방법이 있어 정리해둔다.

참고: https://stackoverflow.com/questions/26554713/how-to-truncate-working-directory-in-prompt-to-show-first-and-last-folder

PS1='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'\'') \$ '

위 스택오버플로 페이지의 솔루션은 좋긴 하지만 처음 두 개 디렉토리 .. 마지막 하나의 디렉토리 로 구성된다. 이것을 조금 바꾸어 처음 1개의 디렉토리 .. 마지막 두개의 디렉토리 으로 만들고 싶다. 아무래도 뒤에 있는 경로가 좀 더 중요하니까. 따라서 스택오버플로 솔루션에서 순서를 바꾸어야 한다.

    PROMPT_PRE='\[\033[38;5;11m\]\u\[\033[38;5;15m\]@\h:\[\033[38;5;6m\]'
    PROMPT_POST='\[\033[38;5;15m\] '
    PWDTRIM1='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'\'')'
    PWDTRIM2='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/).*([^/]*/[^/]*/)|\1..\2|'\'')'
    PS1="${PROMPT_PRE}${PWDTRIM2}${PROMPT_POST}"

PWDTRIM1 이 스택오버플로 버전이고 PWDTRIM2 가 수정된 버전이다.

스크립트의 전체 구조를 보면, ① 먼저 pwd 명령으로 현재 작업 디렉토리 경로를 받아온 다음, ② sed로 $HOME 디렉토리 패턴을 물결(~)로 바꾸고, ③ 이어서 sed로 처음 하나의 디렉토리와 마지막 두개의 디렉토리 경로를 제외한 부분을 쩜쩜(..) 으로 바꾸어준다.

③ 단계가 복잡하니 이것만 자세히 분석해본다.

    PWDTRIM2='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/).*([^/]*/[^/]*/)|\1..\2|'\'')'

빨갛게 표시한 부분 좌우에 '\' 등 외따옴표로 묶인 것들은 escape를 위해 넣은 것들이다. 위의 sed 명령어에 쓰인 패턴매칭 크게 네 부분으로 나눌 수 있다.

^([^/]*/)
슬래시(/) 문자를 제외한 모든 문자 0개 이상으로 이루어진 문자열로 시작하는 패턴을 Back Reference로 구성하여 \1 로 저장. 이 패턴은 처음에 슬래시 문자가 나왔다가 다시 등장하거나, 슬래시 문자가 없다가 등장할 경우 패턴매칭이 끝나기 때문에 첫 번째 디렉토리 경로로 볼 수 있다.
.*
첫 번째 매칭 이후에 나오는 모든 문자들의 패턴. 뒤에 나올 마지막 2개 디렉토리 패턴은 해당 패턴이 가져가므로, 처음 하나 ~ 나중 두개 디렉토리 경로에서 중간 부분이라고 볼 수 있다.
([^/]*/[^/]*/)
[^/]* '슬래시(/)문자를 제외한 모든문자 0개 이상으로 이루어진 문자열' 이후에
/ '슬래시(/)문자'가 나오고
[^/]* 그리고 이어서 '슬래시(/)문자를 제외한 모든 문자 0개 이상으로 이루어진 문자열'이 등장.
/ 이어서 마지막으로 '슬래시(/)문자'가 나온다.
종합해 보면 마지막 2개의 디렉토리를 의미한다. 그리고 이 패턴은 Back Reference로서 /2 에 저장된다.
\1..\2
Back Reference 1번은 작업 디렉토리 경로에서 첫 번째 디렉토리이고, Back Reference 2번은 작업 디렉토리에서 마지막 두 개의 디렉토리이다. 이들 가운데 쩜쩜(..)을 넣도록 하여 문자열을 대치한다.

Regular expression in sed

Programming

sed(1) 에서 사용되는 정규표현식에 대해서 알아보기 위해 Overview of Regular Expression Syntax 의 내용을 옮겨본다.

sed 를 잘 쓰기 위해서는 정규표현식(regexp)을 잘 알아야 한다. 정규표현식이란 주어진 문자열(subject string)에서 왼쪽에서 오른쪽으로 봐가면서 매칭되는 패턴을 의미한다. 정규표현식에서 대부분의 문자들(character)은 일반문자(ordinary)이다. 즉 이 문자들이 패턴 자체이고 주어진 문자열에서 해당 문자들이 나올 경우 매칭되게 된다. 예를 들어 아래와 같은 패턴은,

The quick brown fox

그 자체가 패턴이 된다. 하지만 정규표현식의 강력한 기능은 패턴을 작성함에 있어서 대치(alternatives)와 반복(repetitions)이 가능하다는 점이다. 정규표현식에서 대치와 반복은 특수문자를 써서 표현할 수 있다. 이렇게 특수문자로 쓰인 패턴은 그 자체가 패턴이 되는 것이 아니라 뭔가 특별한 용도를 위해 다르게 처리된다. 그럼 아래에 sed 에서 쓰이는 정규표현식 문법에 대해 정리해 보겠다.

Pattern Meaning
char 그 자체가 패턴이 되는 일반문자를 나타냄.
* * 앞에 나오는(preceding) 정규표현식이 0번 이상 매칭되는 패턴을 의미한다. '앞에 나오는 정규표현식'에는 ①일반문자, ②백슬래시(\)로 시작하는 특수문자, ③점(.), ④(아래에서 설명할)정규표현식 그룹, 그리고 ⑤대괄호로 묶여진 표현식 등이 될 수 있다. GNU 확장규칙(extension) 에서는 정규표현식이 * 뒤에서 나올(postfixed)수도 있다. 예를 들어, a**a* 랑 같다. POSIX 1003.1-2001에 의하면 * 은 어떤 정규표현식이나 서브익스프레션의 시작부분에 등장할 경우 별(*) 그 자신을 의미한다고 되어있다. 하지만 많은 non-GNU 유틸들은 이를 지원하지 않으며, 같은 상황에서 포터블 스크립트들은 \* 을 대신 사용한다.
\+ * 과 동일하지만 매칭되는 패턴의 개수가 1개 이상이다. GNU extension.
\? * 과 동일하지만 매칭되는 패턴의 개수가 0 또는 1개 이다. GNU extension.
\{i\} * 과 동일하지만 매칭되는 패턴의 개수는 정확히 i 번이다. 여기서 i는 10진수 정수지만 0~255 사이의 값을 사용하는것이 안전하다.
\{i,j\} i에서 j 개가 매칭된다.
\{i,\} i 번 이상 매칭된다.
\(regexp\) 내부 regexp를 그룹지을 때 사용한다. 그룹이 사용되는 예를 살펴보면 아래와 같다.
  • postfix operator로서 사용된다. 예를 들어, \(abcd\)* 은 'abcd' 문자열이 0번 이상 나오는 패턴이다. 단, 이는 POSIX 1003.1-2001 에 의한 GNU extenstion 이므로 non-GNU 유틸에서는 지원하지 않는다. 따라서 호환성을 고려한다면 안 쓰는게...
  • (뒤에서 설명할)Back reference 에 사용된다.
. 개행을 포함한 모든 캐릭터와 매칭된다.
^ 어떤 패턴에서 시작 부분의 널문자와 매칭된다. 이게 무슨 말이냐면, 꺾쇄문자(^) 다음에 오는 패턴은 패턴의 시작부분이라는 얘기다.

대부분의 sed 스크립트에서 패턴은 새 행이 시작될 때마다 초기화된다. 따라서 ^#include 의 경우 해당 행은 '#include' 라는 문자열로 시작되어야 하는 것이다. 만일 '#include' 앞에 공백이 있는 행일 경우에는 패턴매칭이 실패한다. 또한 이는 패턴 스패이스에서 원본 컨텐츠가 수정되지 않은 경우, 예를 들어 s 커맨드를 사용할 경우에만 유효하다.

^는 정규표현식이나 서브익스프레션에서 맨 앞에 나오는 특수문자로서도 쓰일 수 있다. POSIX에서 ^를 일반문자로 취급하는것을 허용하기는 하지만 포터블 스크립트에서는 서브익스프레션의 시작부분에 ^를 사용하는 것을 피하는 것이 좋을 것이다.
$ ^ 와 비슷하지만 맨 끝부분 패턴을 나타낸다. $ 도 역시 정규표현식이나 서브익스프레션에서 맨 끝에있는 특수문자를 가리키는데 쓰일 수 있다.
[list]
[^list]
list 내부의 단일 문자와 매칭된다. 예를 들어, [aeiou] 은 모든 모음 문자(vowels)와 매칭된다. listchar1-char2와 같은 표현식 (char1char2사이에 있는 모든 단일 캐릭터) 으로도 사용할 수 있다.

여기서 맨 앞에 ^이 나올 경우 list의 의미를 반전시킨다. 즉, list안에 있는 문자들을 제외한 단일 문자가 된다. 이스케이핑(escape) 관련하여 특수문자인 ]list 안에 포함시키기 위해서는 대괄호 닫음(])을 맨 처음에 넣으면 되고(^가 필요할 경우 ^를 먼저 넣는다), 특수문자 -list 안에 넣기 위해서는 대시(-)를 맨 처음이나 맨 나중에 넣는다. 특수문자 ^ 의 경우에는 첫 문자 다음에 넣도록 한다.

특수문자 $, *, ., [, \ 들은 list 안에서 특수문자(특별한의미)로 동작하지 않는다. 예를 들어 [\*] 이것은 '\' 이나 '*' 문자가 매칭된다. 왜냐하면 \list 안에서 특수문자(특별한의미)로 동작하지 않기 때문이다. 그렇지만 [.ch.], [=a=], [:space:] 들은 list 안에서 그별한 의미를 지니며 각각은 ch 이중문자(collating symbol; ch 체코어?), a 와 동등한 정렬순서를 갖는 문자집합(equivalence class), 공백을 나타내는 문자클래스(character class)를 의미한다. 즉, [list 안에서 ., =, : 등과 함께 쓰였을 경우 특별한 의미를 갖게된다. 또한 POSIXLY_CORRECT 모드가 아닐 경우 \n 이나 \t 과 같은 특수문자들도 list 안에서 특별한 의미를 갖는다. Escape 참고.
regexp1\|regexp2 regexp1 이나 regexp2 를 매칭한다(OR). 소괄호()를 사용하면 여러개를 이어서 사용할 수도 있는 것 같다. 매칭 과정은 왼쪽부터 시작해서 오른쪽으로 가면서 정규표현식들을 검사하고, 제일 처음 매칭된 정규표현식을 사용하게 된다. GNU extension 이다.
\digit 정규표현식 안에서 digit 번째의 소괄호 \(...\) 로 둘러싸인 서브익스프레션과 매칭된다. 이것을 back reference 라고 한다. 서브익스프레션은 암묵적으로 왼쪽에서 오른쪽으로 보면서 \( 이것이 나온 개수에 대하여 넘버링이 되어있다.
\n 개행 문자와 매칭된다.
\char char 와 매칭한다. 여기서 char$, *, ., [, \, ^ 이들 중에 하나이다. 이스케이핑(escape) 용도라는 말임. 참고로 C언어에서의 백슬래시 조합 특수문자 중에 \n 이나 \\ 이런것은 사용할 수 있다. 하지만 \t 는 대부분의 sed 구현에서 빠져있다. 즉, 탭 문자가 아니라 t로 매칭을 시도하게 된다.

정규표현식의 매칭방식은 좌에서 우로 나아가며 매칭하고, 같은 위치에서 두 개 이상의 매칭이 발견되었을 경우에는 긴 쪽을 택한다. 그래서 greedy(탐욕스러운) 하다라고도 한다.

아래는 정규표현식 예제들이다.

'abcdef'
쓰여진 그대로 'abcdef' 와 매칭됨.
'a*b'
0개 이상의 'a'와 그 다음에 'b'가 나오는 경우와 매칭된다. 예를 들면 'b'나 'aaaaab'.
'a\?b'
\? 는 0개 또는 1개 와 매칭되는 것이다. 따라서 'b' 또는 'ab' 와 매칭된다.
a\+b\+
\+ 는 1개 이상과 매칭되는 것이다. 따라서 1개 이상의 'a'와 이어서 1개 이상의 'b'가 오는 경우이다. 가장 짧게 매칭되는 경우가 'ab' 이고 'aaaab', 'abbbbb', 'aaaaaabbbbbb' 이렇게도 가능하다.
'.*/
'.\+'
둘 다 모든 캐릭터 문자열과 매칭되는 경우이다. 단, 위의 케이스는 널 스트링을 포함한 모든 문자열과 매칭되지만, 아래것은 최소한 하나의 문자는 들어있는 문자열과 매칭된다.
'^main.*(.*)'
'main'으로 시작하고, 소괄호로 둘러쌓인 문자열이 이어지는 경우이다. 'n'과 '('과 ')' 이 위치가 반드시 딱 붙어있어야 하는것은 아니다.
'^#'
'#'으로 시작하는 문자열과 매칭된다.
\\$
백슬래시(\) 하나로 끝나는 문자열과 매칭된다. 백슬래시를 두 개 썼지만 하나는 escaping 용도로 쓴 것이다.
'\$'
달러 기호 하나와 매칭된다. 여기에 쓰인 백슬래시도 escaping 용도로 쓰였다.
'[a-zA-Z0-9]'
환경변수 로케일이 C locale 인 경우, 모든 ASCII 문자 혹은 숫자와 매칭된다.
'[^ tab]\+
여기서 tab 은 탭 문자이다. 이 예제는 공백이나 탭 문자를 제외한 한 글자 이상의 캐릭터와 일치한다. 즉, 한 단어(word)를 의미한다.
'^\(.*\)\n\1$'
\(regexp\) 은 그룹을 이루고 back reference 로도 쓰인다. 이 예제는 두 개의 동일한 문자열 사이에 개행문자가 하나 들어간 꼴이다.
'.\{9\}A$'
9개의 문자에 이어서 A가 나오는 패턴이다.
'^.\{15\}A'
16개의 문자로 시작하고, 그 16개 중에 마지막 문자가 A인 경우이다.

'Programming' 카테고리의 다른 글

Bash에서 문자열 검색 조건식 만들기  (0) 2018.07.22
Truncate PWD in prompt (MacOS)  (0) 2018.04.29
How to terminate a background process  (0) 2018.04.24
macOS에 emacs ggtags 설치 및 설정  (0) 2017.10.17
Xcode에 assimp 올리기  (0) 2017.06.06

How to terminate a background process

Programming

가끔 실행중인 프로세스를 종료시키고 싶을 때 Ctrl + c 가 먹지 않는 경우가 있다. 이럴때 나는 Ctrl + z 로 실행중인 프로세스를 백그라운드로 돌려놓고, PID를 알아낸 다음에 SIGKILL 시그널(9번)을 보내는 식으로 처리하곤 했다.

그런데 이것을 좀 쉽게 하는 방법이 있었다.

  • 모든 백그라운드 프로세스 끝내버리기: kill -9 $(jobs -p)
  • 백그라운드 프로세스 하나만 끝내버리기:
    우선 jobs 명령어로 모든 백그라운드 프로세스의 목록을 가져온 다음에,
    kill %1 하면 첫 번째 프로세스를 끝낼 수 있고,
    kill %2 하면 두 번째 프로세스를 끝낼 수 있다.

참고: https://unix.stackexchange.com/questions/104821/how-to-terminate-a-background-process

'Programming' 카테고리의 다른 글

Truncate PWD in prompt (MacOS)  (0) 2018.04.29
Regular expression in sed  (0) 2018.04.25
macOS에 emacs ggtags 설치 및 설정  (0) 2017.10.17
Xcode에 assimp 올리기  (0) 2017.06.06
OpenGL로 원 그리기  (1) 2017.05.27

macOS에 emacs ggtags 설치 및 설정

Programming

macOS에 emacs ggtags 설치 및 설정하는 과정을 정리한다. 성공한 환경은 아래와 같다.

  • macOS Version: macOS Sierra 10.12.6 (16G29)
  • emacs Version: GNU Emacs 25.1.1 (x86_64-apple-darwin13.4.0, NS appkit-1265.21 Version 10.9.5 (Build 13F1911)) of 2016-09-21

1. 설치

1.1 Homebrew로 ruby, ctags 설치

brew install --upgrade ruby
brew install --HEAD ctags

1.2 Homebrew로 GNU Global 설치

brew install global --with-exuberant-ctags

1.3 pygments 플러그인 설치

sudo pip install pygments
sudo pip3 install pygments

pygments는 Syntax Highlighter이다. ctags가 레퍼런스 탐색 기능을 제공하지 않기 때문에 Global에서 pygments 플러그인을 사용하도록 설정해 주어야 한다.

2. 설정

2.1 pygments 설정

시스템 환경 변수(.bashrc 등)에 아래와 같이 설정해준다.

export GTAGSCONF=~/.globalrc
export GTAGSLABEL=pygments

즉, Emacs에는 아래와 같이 해주면 된다.

(setenv "GTAGSCONF" (concat (expand-file-name "~") "./globalrc"))
(setenv "GTAGSLABEL" "pygments")

만일 ~/.globalrc 파일이 없다면, /usr/local/share/gtags/gtags.conf 파일을 복사하여 ~/.globalrc 로 만든다. 그리고 :ctagscom= 을 검색하여 아래와 같이 바꿔준다(2개 정도 나옴).

exuberant-ctags|plugin-example|setting to use Exuberant Ctags plug-in parser:\
  :tc=common:\
  :ctagscom=/usr/local/opt/ctags/bin/ctags:\

...

pygments-parser|Pygments plug-in parser:\
  :ctagscom=/usr/local/opt/ctags/bin/ctags:\

2.2 Emacs 설정

(and
 (require 'ggtags nil t)
 (message "ggtags initializing...")
 (autoload 'ggtags-mode "ggtags"
   "Minor mode for browsing source code using GLOBAL" t))

(eval-after-load 'ggtags
  '(progn
     (add-hook 'c-mode-common-hook
               (lambda ()
                 (when (derived-mode-p 'c-mode 'c++-mode 'java-mode 'objc-mode)
                   (ggtags-mode 1))))
     (add-hook 'python-mode-hook (lambda () (ggtags-mode 1)))

     ;; Set GTAGS variables
     (setenv "GTAGSCONF" (concat (expand-file-name "~") "/.globalrc"))
     (setenv "GTAGSLABEL" "pygments")

     (define-key ggtags-mode-map (kbd "C-c C-f") 'ggtags-find-file)
     (define-key ggtags-mode-map (kbd "C-c g") 'ggtags-grep)
     ))

(provide 'koodev-ggtags)

3. 실행

프로젝트 폴더로 가서 소스파일을 하나 열고 M-x ggtags-find-tags-dwim 을 실행하면 루트 폴더의 위치를 묻는데, 프로젝트 루트를 지정해주면 잠시 후 G* 파일들이 생성된다.

단축키 등의 자세한 정보는 다음 링크를 참고한다: https://github.com/leoliu/ggtags

4. 기타

ImportError: No module named pygments 이런 에러가 뜨는 경우, 우선은 pygments 플러그인을 잘 설치했나 확인해보고, 실행 환경에서 python이 어느 경로로 잡혀있는지 확인해본다. 내 경우에는 시스템에 python이 두 개가 설치되어 있었는데, 터미널을 열 경우와 Emacs나 다른 유틸(GNU Global)을 실행하는 시점에서 python 경로가 달라 위와 같은 에러가 떴다. 추가로 설치된 python을 삭제하고 pygments 등을 재설치해 주었더니 문제가 해결되었다.

References

'Programming' 카테고리의 다른 글

Regular expression in sed  (0) 2018.04.25
How to terminate a background process  (0) 2018.04.24
Xcode에 assimp 올리기  (0) 2017.06.06
OpenGL로 원 그리기  (1) 2017.05.27
Swift3 - result unused warning 없애기  (0) 2017.05.23

Xcode에 assimp 올리기

Programming

OpenGL 3D 모델 관련 코드를 연습해보다가 웹에 공유된 모델 데이터를 코드로 읽어들이는 작업이 필요하게 되었다. 참고하고 있는 문서에서는 assimp 외부 라이브러리를 사용하고 있었는데, 윈도우 기반으로 설명하고 있어서 Xcode에 적용하는 방법은 직접 알아낼 수 밖에 없었다. 이 포스트에서는 Xcode에 MacOS용 assimp를 적용하는 방법을 정리한다.

아래는 assimp 라이브러리를 사용하여 nanosuit.obj 를 읽어들여 렌더링한 결과이다. 텍스처는 아직 연결하지 못했다.

한편, 애플은 ModelIO 라는 API를 제공하고 있었는데 나한테는 assimp보다 사용성이 직관적이지 않고 이해하고 적용하기까지 많은 시간이 걸리는 듯 하여 포기했다.

Assimp 다운로드

assimp 페이지를 방문하여 코드를 다운로드 받는다. 나는 최신 버전을 git으로 받았다.

git clone git://github.com/assimp/assimp.git

Cmake 설치

Assimp를 빌드하려면 Cmake를 설치해야 한다. GUI버전을 다운로드 받아도 될지 모르겠지만, 나는 brew를 사용하여 설치했다.

brew install cmake

Assimp 빌드

여기서부터 좀 삽질을 했었는데, 그냥 Cmake를 돌리면 컴파일러가 지정되어 있지 않다는 등 알 수 없는 에러가 나온다. 아래는 assimp 소스 폴더에서 cmake CMakeLists.txt -G "Xcode" 를 실행했을 때 나왔던 에러이다.

-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error at CMakeLists.txt:38 (PROJECT):
  No CMAKE_C_COMPILER could be found.



CMake Error at CMakeLists.txt:38 (PROJECT):
  No CMAKE_CXX_COMPILER could be found.



-- Configuring incomplete, errors occurred!
See also "/Users/koodev/Workspace/Source/assimp/CMakeFiles/CMakeOutput.log".
See also "/Users/koodev/Workspace/Source/assimp/CMakeFiles/CMakeError.log".

GUI 버전의 Cmake를 돌려도 비슷한 에러가 뜬다. 나한테 유효했던 해결책은 아래와 같이 xcode-select 로 developer tool의 기준이 되는 경로를 지정해 주는 것이다. 이 방법을 알아내는데 참고한 사이트는 다음과 같다: https://github.com/nodejs/node-gyp/issues/569#issuecomment-255589932

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
cmake CMakeLists.txt -G "Xcode"

그러면 한동안 빌드가 돌아가고 Assimp.xcodeproj 도 나온다. 내 Mac에 Xcode는 한번밖에 설치한 적이 없을텐데 왜 이게 문제가 되었는진 모르겠다. 힘들게 빌드는 했지만 잘 이후에도 큰 난관이 2개나 더 있었다.

프로젝트에 Assimp 가져오기

Xcode에 외부 라이브러리를 추가하는 방법은 두 가지 정도가 될 수 있을 것 같다. 하나는 해당 라이브러리를 Sub Projext로 구성하는 것이고, 다른 하나는 직접 라이브러리 파일을 추가하는 것이다. 앞의 방식을 사용하면 디버깅을 할 수 있다는 장점이 있다. (뭐가 쉬울까) 고민 끝에 나는 뒤의 방식으로 진행했다.

우선 프로젝트 루트 폴더 밑에 assimp라는 폴더를 만들고 여기에 assimp 소스의 include 폴더와 lib/Debug 폴더를 가져와 아래와 같이 구성한다. lib 폴더에 assimp의 lib/Debug 안에 있는 .a파일과 링크들을 넣었다.

.
├── OpenGLScratch
├── OpenGLScratch.xcodeproj
└── assimp
    ├── include
    │   └── assimp
    │       ├── Compiler
    │       └── port
    │           └── AndroidJNI
    └── lib

그리고 Finder를 열어 assimp 폴더를 Xcode로 그래그하여 옮긴다. 아래와 같은 상태로 만드는 것이다.

프로젝트의 Build Phases - Link Binary With Libraries로 가보면 Xcode가 친절하게도 방금 전에 추가한 라이브러리들을 링크하게 설정해 놓았다.

그치만 헤더 파일의 경로는 자동으로 잡아주지 않기 때문에 이것을 직접 해 주어야 한다. Build Settings - Header Search Paths 에 있다.

끝?

이제 프로젝트에서 assimp라는 외부 라이브러리를 사용할 수 있는 상태가 된 것이다. 하지만 코드에서 assimp를 쓰기 위해서 해야 할 일이 좀 더 있다. assimp 인터페이스가 C++ 이기 때문에 Objective-C++ 로 wrapper 인터페이스를 만들어야하고, Swift를 사용할 경우에는 Bridging Header에 새로 만든 wrapper 헤더를 넣어주어야 한다.

참고