みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

TinyGoをSTM32F4Dicoveryで動かす

はじめに

TinyGoの勉強を兼ねてSTM32F4Dicoveryへ移植してみる。

LチカとHelloWorldを動かせれば良いので、次のペリフェラルのみ動かす。

  • GPIO
  • UART(USART2)

実際に動かしたサンプル。

  • examples/brinky1(Lチカ)
  • examples/brinky2(gofuncの並列Lチカ)
  • examples/serial(HelloWorld)
  • examples/echo(UARTでのエコー)
  • examples/test(golangの基本機能のテスト)

クロック設定は、168MHzに固定

移植作業

追加したファイル

移植に当たって次のファイルを追加した。

ファイル名 ディレクト 概要
stm32f4disco.json targets ターゲット定義
stm32f407.ld targets リンカスクリプト
runtime_stm32f407.go src/runtime クロック初期化など
machine_stm32f407.go src/machine マシン(SoC毎)定義
board_stm32f4disco.go src/machine ボード(ペリフェラル)定義

たった5つのファイルで新しいボードに対応できた。

詳しく見れているわけではないが、抽象化層がよくできている印象。

修正したファイル

既存のファイルの変更はsrc/machine/machine_stm32.goのみ。 GPIOのポートの定義を追加した。

stm32f4disco.json

ターゲットをビルドするための定義ファイル。 ツールチェインやビルド設定、ターゲットへの書き込み方法、デバッガの設定などを定義する。

'inherit'でベースになる定義ファイルを取り込むことができる。

{
  "inherits": ["cortex-m"],
  "llvm-target": "armv7em-none-eabi",
  "build-tags": ["stm32f4disco", "stm32f407", "stm32"],
  "cflags": [
    "--target=armv7em-none-eabi",
    "-Qunused-arguments"
  ],
  "ldflags": [
    "-T", "targets/stm32f407.ld"
  ],
  "extra-files": [
    "src/device/stm32/stm32f407.s"
  ],
  "flash": "openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c 'program {hex} reset exit'",
  "ocd-daemon": ["openocd", "-f", "interface/stlink.cfg", "-f", "target/stm32f4x.cfg"],
  "gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
}

'build-tags'で、'ボード名'、'マシン名'、'アーキテクチャ'を設定する。 Linuxカーネルを覗いた事がある人なら、'board'、'mach'、'arch'のようにイメージすれば良さそう。 ここに指定した文字列を元に、ビルドするターゲットのソースファイルを選択しているっぽい。

使用するリンカスクリプトもここで定義する。

stm32f407.ld

RAMは192K使えるはずなんだけど、とりあえず128Kにしといた。

MEMORY
{
    FLASH_TEXT (rw) : ORIGIN = 0x08000000, LENGTH = 1M
    RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
}

_stack_size = 4K;

INCLUDE "targets/arm.ld"

runtime_stm32f407.go

クロック関連の初期化や処理を書く。 スリープやgofuncのスケジューラから呼ばれて、タイマーなどで実際のタイミングを取るための処理。

ここで実装した関数は次の通り。

  • init
  • putchar
  • initCLK
  • initTIM3
  • initTIM7
  • sleepTicks
  • ticks
  • timerSleep
  • handleTIM3
  • handleTIM7

ビルド対象選択のギミック

ソースファイルの先頭に次のようなコメントがある。

// +build stm32,stm32f407

これがビルドするターゲットが「stm32,stm32f407」だった場合にこのファイルをビルドするという宣言になっているらしい。

クロック初期化

initCLKはクロック初期化の処理。platformioや他のベアメタル系の処理を参考(ほぼパクリ)に実装した。

ここで重要なことはPLLの設定とそれぞれのプリスケーラの設定の計算。

システムクロックが168MHzになるように設定している。 外部クロックである`HSE'の値は8MHzなので、これを基準にクロックを計算する。

const (
    HSE_STARTUP_TIMEOUT = 0x0500
    /* PLL Options - See RM0090 Reference Manual pg. 95 */
    PLL_M = 8    /* PLL_VCO = (HSE_VALUE or HSI_VLAUE / PLL_M) * PLL_N */
    PLL_N = 336
    PLL_P = 2    /* SYSCLK = PLL_VCO / PLL_P */
    PLL_Q = 7    /* USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q */
)

また、それぞれのクロックドメインを次のように設定した。

クロック 周波数
SYSCLK 168mhz
HCLK 168mhz
APB2(PCLK2) 84mhz
APB1(PCLK1) 42mhz

Sleep処理

Lチカなどで一定時間待ち合わせたりする際に必要なsleepの処理のためにTIM3を使用する

TIM3はAPB1に接続されているので、内部クロックソースの場合は42mhzになるかと思いきや、 タイマーは自分でPLLを持っているらしく何も設定しない場合は42mhzの倍の84mhzで動作するらしい。

このタイマーは、プリスケーラで分周したクロックで、ARRに設定したカウント分に到達した時に割り込みを発生させることができる。

tinygoでのsleepはマイクロ秒単位で指定なので、たとえば500ミリ秒スリープしたい場合は500000000が渡されてくる。

参考にした実装では、タイマーのクロックを10Khzに分周して、ARRを0.1ミリ秒単位で設定することで、 スリープ解除したいタイミングで割り込みが発生するようにしていた。

なのでこのようなコードになる。

// ticks are in microseconds
func timerSleep(ticks uint32) {
    timerWakeup = false

    // CK_INT = APB1 x2 = 84mhz
    // prescale counter down from 84mhz to 10khz aka 0.1 ms frequency.
    stm32.TIM3.PSC = 84000000/10000 - 1 // 8399

    // set duty aka duration
    arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms
    if arr == 0 {
        arr = 1 // avoid blocking
    }
    stm32.TIM3.ARR = stm32.RegValue(arr)

    // Enable the hardware interrupt.
    stm32.TIM3.DIER |= stm32.TIM_DIER_UIE

    // Enable the timer.
    stm32.TIM3.CR1 |= stm32.TIM_CR1_CEN

    // wait till timer wakes up
    for !timerWakeup {
        arm.Asm("wfi")
    }
}

84Mhzから10Khzを作るためにプリスケーラレジスタのPSCに8399を設定。 10Khzは1秒に10000回振幅するので、1回の振幅に0.1ミリ秒かかる計算になる。

引数で渡されたtickはマイクロ秒単位なので、0.1ミリ秒単位になるようにARRを設定する。 割り込みハンドラの中で、スリープ解除フラグを立てるという前提で、フラグが経っていない時はwfiで寝る。

なかなかに分かりやすい構造となっている。

割り込みを有効化するためには、NVICに対する設定を行なう次の関数を使用する。

   arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
    arm.EnableIRQ(stm32.IRQ_TIM3)

そしてハンドラは次のようにする。

//go:export TIM3_IRQHandler
func handleTIM3() {
    if (stm32.TIM3.SR & stm32.TIM_SR_UIF) > 0 {
        // Disable the timer.
        stm32.TIM3.CR1 &^= stm32.TIM_CR1_CEN

        // clear the update flag
        stm32.TIM3.SR &^= stm32.TIM_SR_UIF

        // timer was triggered
        timerWakeup = true
    }
}

//go:export TIM3_IRQHandlerのコメント行がキモとなっていて、この次に定義された関数がTIM3_IRQHandlerとしてエクスポートされる。 つまり、割り込みハンドラとして登録されるようになっている。

gofuncスケジューラのためのtickカウンタ

examples/blinky2では2つのLEDをgofuncを使用して2つの関数からそれぞれ制御する。 tinygoは内部にスケジューラを持っていて、スケジューラがタイミングを取るためにticks関数を呼び出している。

ticks関数は、ボードが起動してからのカウントをマイクロ秒単位で取得する。

// number of ticks (microseconds) since start.
func ticks() timeUnit {
    // milliseconds to microseconds
    return tickCount * 1000
}

ここで返すカウント値は、TIM7を使って1ミリ秒ごとにカウントアップすることで作る。(なので戻り値は1000倍している)

TIM7の設定は基本的にTIM3と同じ。違うのはARRが10固定になっている点。

PSCで10Khzを作って、0.1ミリ秒の振幅が10回行われた時、つまり1ミリ秒に1回割り込みが発生する。 その中でカウンタを増やす。

// Enable the TIM7 clock.(tick count)
func initTIM7() {
    stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_TIM7EN

    // CK_INT = APB1 x2 = 84mhz
    stm32.TIM7.PSC = 84000000/10000 - 1     // 84mhz to 10khz(0.1ms)
    stm32.TIM7.ARR = stm32.RegValue(10) - 1 // interrupt per 1ms

    // Enable the hardware interrupt.
    stm32.TIM7.DIER |= stm32.TIM_DIER_UIE

    // Enable the timer.
    stm32.TIM7.CR1 |= stm32.TIM_CR1_CEN

    arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
    arm.EnableIRQ(stm32.IRQ_TIM7)
}
...(snip)...

//go:export TIM7_IRQHandler
func handleTIM7() {
    if (stm32.TIM7.SR & stm32.TIM_SR_UIF) > 0 {
        // clear the update flag
        stm32.TIM7.SR &^= stm32.TIM_SR_UIF
        tickCount++
    }
}

machine_stm32f407.go

マシン(SoC)固有の処理を書く。 ペリフェラルのドライバはここに書くことになる。

今回は次のドライバを実装している。

  • GPIO
  • UART

GPIOドライバ

GPIOドライバでは次の関数を実装している。

  • getPort
  • enableClock
  • Configure
  • setAltFunc
  • Set

この内外部から呼び出される可能性があるのは大文字で始まっているConfigureSetのみ。 golangでは大文字で始まるシンボルがエクスポートされるらしい。

getPort

この関数ではGPIOA〜GPIOIまでのレジスタアクセス用の構造体を取得できる。

enableClock

この関数はAHB1のGPIOxENを有効にしデバイスを有効化する。

Configure

この関数は外部から呼び出され、ピンの方向やプッシュプルなどの設定を行なう。

指定できるフラグは次のようになっている。

フラグ 機能 備考
GPIO_OUTPUT 出力
GPIO_INPUT 入力、GPIO_INPUT_PULLDOWNと同じ
GPIO_INPUT_FLOATING 入力でフローティング
GPIO_INPUT_PULLDOWN 入力でプルダウン
GPIO_INPUT_PULLUP 入力でプルアップ
GPIO_UART_TX UARTのTX設定 UARTドライバからのみ呼ばれる
GPIO_UART_RX UARTのRX設定 UARTドライバからのみ呼ばれる

setAltFunc

GPIOのAlternative Functionを設定する。

Set

出力ピンのHI/LOを設定する。

UARTドライバ

UARTドライバはほぼmachine_stm32f103xx.go(BluePill向け)のドライバを拝借した。

ボーレートの設定の部分だけSTM32F4Discoveryに合わせて設定した。 本来であれば9600や38400など一般的な設定は受け付けるようにするべきだが、 今回は115200に固定とした。

ボーレートを決定するにはBRRレジスタを設定する必要がある。

BRRに設定できる値はRM0090のTable 134〜143に一覧されている。

USART2はAPB2に接続されているので、クロックは42Mhzとなる。USART2_CR1のOVER8は0に設定されているのでTable 142の値を使用できる。

f:id:mickey_happygolucky:20190416115524p:plain
Table 142(抜粋)

BRRは指数部が4ビットの固定小数点のため、下記のように計算できる。

(整数部<<4)+(指数部*16)

なのでこのように実装している。

// Configure the UART.
func (uart UART) Configure(config UARTConfig) {
    // Default baud rate to 115200.
    if config.BaudRate == 0 {
        config.BaudRate = 115200
    }

    // pins
    switch config.TX {
    default:
        // use standard TX/RX pins PA2 and PA3
        GPIO{UART_TX_PIN}.Configure(GPIOConfig{Mode: GPIO_UART_TX})
        GPIO{UART_RX_PIN}.Configure(GPIOConfig{Mode: GPIO_UART_RX})
    }

    // Enable USART2 clock
    stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_USART2EN

    /*
     Set baud rate(115200)
     OVER8 = 0, APB2 = 42mhz
     +----------+--------+
     | baudrate | BRR    |
     +----------+--------+
     | 1200     | 0x88B8 |
     | 2400     | 0x445C |
     | 9600     | 0x1117 |
     | 19200    | 0x88C  |
     | 38400    | 0x446  |
     | 57600    | 0x2D9  |
     | 115200   | 0x16D  |
     +----------+--------+
   */
    stm32.USART2.BRR = 0x16c

    // Enable USART2 port.
    stm32.USART2.CR1 = stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE

    // Enable RX IRQ.
    arm.SetPriority(stm32.IRQ_USART2, 0xc0)
    arm.EnableIRQ(stm32.IRQ_USART2)
}

デバッグ中に0x16cをBRRに設定したままコミットしてしまった。 0x16dを入れても問題なく動く。

board_stm32f4disco.go

このファイルではボードに実装されているピンやペリフェラルを定義する。

// +build stm32,stm32f4disco

package machine

const (
    PA0  = portA + 0
    PA1  = portA + 1
    PA2  = portA + 2
    PA3  = portA + 3
    PA4  = portA + 4
    PA5  = portA + 5
    PA6  = portA + 6
    PA7  = portA + 7
    PA8  = portA + 8
    PA9  = portA + 9
    PA10 = portA + 10
    PA11 = portA + 11
    PA12 = portA + 12
    PA13 = portA + 13
    PA14 = portA + 14
    PA15 = portA + 15

    PB0  = portB + 0
    PB1  = portB + 1
    PB2  = portB + 2
    PB3  = portB + 3
    PB4  = portB + 4
    PB5  = portB + 5
    PB6  = portB + 6
    PB7  = portB + 7
    PB8  = portB + 8
    PB9  = portB + 9
    PB10 = portB + 10
    PB11 = portB + 11
    PB12 = portB + 12
    PB13 = portB + 13
    PB14 = portB + 14
    PB15 = portB + 15

    PC0  = portC + 0
    PC1  = portC + 1
    PC2  = portC + 2
    PC3  = portC + 3
    PC4  = portC + 4
    PC5  = portC + 5
    PC6  = portC + 6
    PC7  = portC + 7
    PC8  = portC + 8
    PC9  = portC + 9
    PC10 = portC + 10
    PC11 = portC + 11
    PC12 = portC + 12
    PC13 = portC + 13
    PC14 = portC + 14
    PC15 = portC + 15

    PD0  = portD + 0
    PD1  = portD + 1
    PD2  = portD + 2
    PD3  = portD + 3
    PD4  = portD + 4
    PD5  = portD + 5
    PD6  = portD + 6
    PD7  = portD + 7
    PD8  = portD + 8
    PD9  = portD + 9
    PD10 = portD + 10
    PD11 = portD + 11
    PD12 = portD + 12
    PD13 = portD + 13
    PD14 = portD + 14
    PD15 = portD + 15

    PE0  = portE + 0
    PE1  = portE + 1
    PE2  = portE + 2
    PE3  = portE + 3
    PE4  = portE + 4
    PE5  = portE + 5
    PE6  = portE + 6
    PE7  = portE + 7
    PE8  = portE + 8
    PE9  = portE + 9
    PE10 = portE + 10
    PE11 = portE + 11
    PE12 = portE + 12
    PE13 = portE + 13
    PE14 = portE + 14
    PE15 = portE + 15

    PH0 = portH + 0
    PH1 = portH + 1
)

const (
    LED         = LED_BUILTIN
    LED1        = LED_GREEN
    LED2        = LED_ORANGE
    LED3        = LED_RED
    LED4        = LED_BLUE
    LED_BUILTIN = LED_GREEN
    LED_GREEN   = PD12
    LED_ORANGE  = PD13
    LED_RED     = PD14
    LED_BLUE    = PD15
)

// UART pins
const (
    UART_TX_PIN = PA2
    UART_RX_PIN = PA3
)

特に難しいところはない。

tinygoコマンド

ソースコードをビルドしたり、プログラムを実機に書き込んだりするにはtinygoコマンドを使用する。

単にビルドするのであればbuildサブコマンドを使用する。

$ tinygo build -o test.elf --target stm32f4disco examples/echo

プログラムを書き込む場合はflashサブコマンドを使用する。flashではビルドから書き込みまでやってくれるので便利。

$ tinygo flash --target stm32f4disco examples/echo

GDBデバッグする場合はgdbサブコマンドを使用する。これも必要に応じてビルド、書き込みも行ってくれる。

$ tinygo gdb --target stm32f4disco examples/echo

シンボルデバッグは正直使えないため、レジスタの確認が主な用途だった。

Pull Request

せっかく作ったのでプルリクを送ってみた。

CONTRIBUTING.mdを読んでなかった ため、masterブランチに向けてプルリクを作成したり(devブランチに向けないとダメ)、 go fmtを実行していなかったりといろいろ合ったが、中の人が丁寧にアドバイスをくれた。

最終的には中の人がいろいろと手直しをしてくれてdevブランチにマージされた。

とてもいい経験になった。

まとめ

Golang初心者でも、なんとかSTM32F4DiscoveryでTinyGo動かすことができた。 ビルドシステムやコンパイラがなかなか良くできている印象。

ハードウェアの抽象化レベルが高いのか、少しのコードを足すだけで新しいボードに対応できるのはスゴいと思った。

あと、中の人がとても親切。

ただ、GDBが使えるとはいえ、シンボルでバッグがまともに使えないので(goのシンボルとは1:1にならないので?)、 UARTが使えるようになるまではデバッグがしんどかった。

環境が整ってくればかなり使えるのではないかと思う。 意外とGolangのコードを修正して実機で確認するというサイクルが手軽に回せるので開発しやすいと思った。

tinygoでBluePill

はじめに

tinygoを作ったので、BluePillで動かしてみる。

ここの手順を参考にする。

テストを実行

このコマンドはどこのディレクトリでも実行可能。

$ tinygo run examples/test

バイスターゲットの生成

これは最初にやっておく必要があるらしい。

$ cd $GOPATH/src/github.com/tinygo-org/tinygo
$ make gen-device

BluePillとST-Link V2の接続

次のように接続する。

ST-Link V2 Pin ターゲット
GND 20 GND
TCK 9 CLK
TMS 7 IO
TVCC 1 3.3V

ST-Link V2とPCをUSBケーブルで接続する。

次にBluePillのマイクロUSBのポートとPCをUSBで接続し、ボードに電源を供給する。

サンプルプログラムを書き込む

次のコマンドでBluePillにLチカプログラムを書き込む。

$ tinygo flash -target=bluepill examples/blinky1
Open On-Chip Debugger 0.10.0+dev-00664-g8417a569 (2019-01-27-16:38)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
WARNING: interface/stlink-v2.cfg is deprecated, please switch to interface/stlink.cfg
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
Info : clock speed 1000 kHz
Info : STLINK V2J14S3 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.217323
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000130 msp: 0x20000d50
** Programming Started **
auto erase enabled
Info : device id = 0x20036410
Info : flash size = 64kbytes
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000d50
wrote 3072 bytes from file /tmp/tinygo866341559/main.hex in 0.231905s (12.936 KiB/s)
** Programming Finished **
** Resetting Target **
shutdown command invoked

LEDがチカチカしたら成功。

Blinky1のソース

ちなみに、書き込んだBlinky1のソースは$GOPATH/src/github.com/tinygo-org/tinygo/src/examples/blinky1にある。

package main

// This is the most minimal blinky example and should run almost everywhere.

import (
    "machine"
    "time"
)

func main() {
    led := machine.GPIO{machine.LED}
    led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
    for {
        led.Low()
        time.Sleep(time.Millisecond * 500)

        led.High()
        time.Sleep(time.Millisecond * 500)
    }
}

まとめ

tinygoが正しく作られてさえいれば難しいところはない。

Ubuntu 18.04でllvm-7系パッケージを削除する方法

はじめに

地味にデスクトップ環境が依存しているので引っこ抜くのが大変

削除方法

gvfs-daemonsがlibllvm7に依存しているらしいのでちょっと大変

まずは消せるだけ消す。

$ sudo apt purge -y clang-7 lldb-7 lld-7
$ sudo apt purge -y libllvm-7-ocaml-dev llvm-7-dev llvm-7-doc llvm-7-examples llvm-7-runtime
$ sudo apt purge llvm-7 
$ sudo apt purge -y clang-7 clang-tools-7 clang-7-doc libclang-common-7-dev libclang-7-dev libclang1-7 clang-format-7 python-clang-7
$ sudo apt purge -y lldb-7 lld-7 libc++-7-dev libc++abi-7-dev 
$ sudo apt purge -y llvm-7-dev

gvfsを消す。

$ sudo apt purge -y gvfs
$ sudo apt purge -y libllvm7 

これでできた。

デスクトップのインストール

このままでは、デスクトップ環境に必要なパッケージがautoremoveの対象となってしまう。

以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  apg apturl-common bolt camlp4 cheese-common gir1.2-accountsservice-1.0
  gir1.2-gck-1 gir1.2-gcr-3 gir1.2-gdesktopenums-3.0 gir1.2-gdm-1.0
  gir1.2-gnomebluetooth-1.0 gir1.2-gnomedesktop-3.0 gir1.2-gweather-3.0
  gir1.2-nm-1.0 gir1.2-nma-1.0 gir1.2-totemplparser-1.0 gir1.2-upowerglib-1.0
  gjs gkbd-capplet gnome-control-center-faces gnome-session-common
  gnome-shell-common gnome-themes-extra gnome-themes-extra-data
  gnome-todo-common gnome-video-effects grilo-plugins-0.3-base
  gtk2-engines-pixbuf gvfs-libs ledit libbrotli1 libcamlp4-ocaml-dev
  libcdio-cdda2 libcdio-paranoia2 libclutter-1.0-common libcogl-common
  libcolord-gtk1 libctypes-ocaml libctypes-ocaml-dev libdazzle-1.0-0
  libdrm-amdgpu1 libdrm-nouveau2 libdrm-radeon1 libegl-mesa0 libegl1
  libegl1-mesa libexiv2-14 libfindlib-ocaml libfindlib-ocaml-dev libfontenc1
  libgail-3-0 libgbm1 libgdm1 libgexiv2-2 libgjs0g libglapi-mesa libgles2
  libglvnd0 libgnome-autoar-0-0 libgnome-todo libgnomekbd-common libgnomekbd8
  libgom-1.0-0 libgraphene-1.0-0 libgrilo-0.3-0 libinput-bin libinput10
  liblirc-client0 liblua5.3-0 libmediaart-2.0-0 libmozjs-52-0 libmtdev1
  libmtp-common libmtp-runtime libmtp9 libnss-myhostname libsysmetrics1
  libtracker-sparql-2.0-0 libwayland-egl1-mesa libwayland-server0 libwoff1
  libxcb-dri2-0 libxcb-dri3-0 libxcb-glx0 libxcb-icccm4 libxcb-image0
  libxcb-keysyms1 libxcb-present0 libxcb-randr0 libxcb-render-util0
  libxcb-res0 libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xkb1 libxcb-xv0
  libxfont2 libxkbcommon-x11-0 libxklavier16 libxshmfence1 libxvmc1
  libxxf86dga1 mutter-common ocaml-base-nox ocaml-compiler-libs ocaml-findlib
  ocaml-interp ocaml-nox python-lldb-7 python3-macaroonbakery python3-nacl
  python3-protobuf python3-pymacaroons python3-rfc3339 python3-tz
  shotwell-common totem-common ubuntu-system-service x11-apps
  x11-session-utils x11-xkb-utils xfonts-base xfonts-encodings xfonts-scalable
  xfonts-utils xinit xinput xserver-common xserver-xorg-legacy yelp-xsl
  zenity-common
これを削除するには 'sudo apt autoremove' を利用してください。
以下のパッケージは「削除」されます:
  apturl* cheese* deja-dup* gdm3* gir1.2-mutter-2* gir1.2-totem-1.0*
  gir1.2-webkit2-4.0* gnome-calendar* gnome-control-center*
  gnome-getting-started-docs* gnome-initial-setup* gnome-online-accounts*
  gnome-session-bin* gnome-shell* gnome-startup-applications* gnome-todo*
  gnome-user-docs* gnome-user-guide* gstreamer1.0-clutter-3.0*
  gstreamer1.0-gl* gvfs-daemons* libcheese-gtk25* libcheese8*
  libclutter-1.0-0* libclutter-gst-3.0-0* libclutter-gtk-1.0-0*
  libcogl-pango20* libcogl-path20* libcogl20* libedataserverui-1.2-2* libgl1*
  libgl1-mesa-dri* libgl1-mesa-glx* libglu1-mesa* libglx-mesa0* libglx0*
  libgoa-backend-1.0-1* libgstreamer-gl1.0-0* liblldb-7* libllvm7*
  libmutter-2-0* libtotem0* libwebkit2gtk-4.0-37* libxatracker2* libyelp0*
  mutter* shotwell* totem* totem-plugins* ubuntu-docs*
  ubuntu-release-upgrader-gtk* ubuntu-session* update-manager*
  update-notifier* x11-utils* xorg* xserver-xephyr* xserver-xorg*
  xserver-xorg-core* xserver-xorg-input-all* xserver-xorg-input-libinput*
  xserver-xorg-input-wacom* xserver-xorg-video-all* xserver-xorg-video-amdgpu*
  xserver-xorg-video-ati* xserver-xorg-video-fbdev* xserver-xorg-video-intel*
  xserver-xorg-video-nouveau* xserver-xorg-video-qxl*
  xserver-xorg-video-radeon* xserver-xorg-video-vesa*
  xserver-xorg-video-vmware* xwayland* yelp* zenity*
アップグレード: 0 個、新規インストール: 0 個、削除: 75 個、保留: 0 個。

これでは困るので、次のようにする。

$ sudo apt install -y gvfs ubuntu-desktop

これで一応次回もデスクトップ環境で起動してくれる。

まとめ

最後にubuntu-desktopをインストールすれば万事解決(ぉ

Ubuntu 18.04にtinygoをインストールする(2019/04編)(4/7追記)

はじめに

Ubuntu18.04でtinygoをインストールしようとしたらハマったのでメモっておく。

基本はここの手順。

Requirement

  • Go 1.11+
  • LLVM 7 (for example, from apt.llvm.org

あとは、ターゲットのツールチェイン。

  • ARM Cortex-M
  • AVR(Arduino)
  • WebAssembly

詳細は先述のリンク元を参照。

筆者環境はarm-none-eabiがインストール済み。

goのインストール

いろいろ手段があるが、インストーラによるインストールが一番便利だった。

$ sudo apt install wget git
$ wget -q https://storage.googleapis.com/golang/getgo/installer_linux
$ chmod +x installer_linux
$ ./installer_linux -version 1.11.7

GOPATHの設定は.bash_profileに自動的に作成される。

このインストーラ意外と優秀で-version 1.11.7とかやると、特定のバージョンをインストールしてくれる。

引数なしの場合は最新バージョン。今回は1.12がインストールされた。

(4/7 追記)

ここで1.11系のgoをインストールしておく必要がある。現時点では1.11.7が最新。

そうしないと、tinygoの実行時に次のエラーが発生する。

$ tinygo flash -target=bluepill examples/blinky1
.go/src/syscall/js/func.go:74:3: todo: unknown expression: select blocking []

既にインストールしてしまった場合はgoを入れ直す必要がある。 このインストーラでインストールした場合は~/.goを削除すれば良い。

$ rm -rf ~/.go
$ ./installer_linux -version 1.11.7

GOPATHの設定

UbuntuGUI環境では、.bash_profileはログイン時にしか読み込まれないので、 一度ログインし直すか、新しく開いた端末で次のコマンドを実行する。

$ source ${HOME}/.bash_profile

.bash_profileに追加された内容を.bashrcに移動してしまうというのも手。

export PATH=$PATH:/home/mickey/.go/bin

export GOPATH=/home/mickey/go

export PATH=$PATH:/home/mickey/go/bin

llvm-7のインストール

ここを参考にした。

sources.listの追加

llvm 7.0.1をインストールするために、/etc/apt/sources.list.d/llvm.listを次の内容で作成する。

# i386 not available
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main
# 7
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main
# 8
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main

これがないと、7.0.0がインストールされる。

$ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
$ sudo apt update

これも忘れずに。

パッケージのインストール

Install (stable branch)に書いてあったものすべて入れた。

$ sudo apt install clang-7 lldb-7 lld-7
$ sudo apt install libllvm-7-ocaml-dev libllvm7 llvm-7 llvm-7-dev llvm-7-doc llvm-7-examples llvm-7-runtime
$ sudo apt install clang-7 clang-tools-7 clang-7-doc libclang-common-7-dev libclang-7-dev libclang1-7 clang-format-7 python-clang-7
$ sudo apt install lldb-7 lld-7 libc++-7-dev libc++abi-7-dev 

tinygoのインストール

まずはgo getを実行する。が、これは必ず失敗する(した)。

$ go get -u github.com/tinygo-org/tinygo

エラー発生(その1)

次のようなエラーが発生した。

$ go get -u github.com/tinygo-org/tinygo
# tinygo.org/x/go-llvm
../../go/src/tinygo.org/x/go-llvm/analysis.go:17:10: fatal error: llvm-c/Analysis.h: No such file or directory
 #include "llvm-c/Analysis.h" // If you are getting an error here read bindings/go/README.txt
          ^~~~~~~~~~~~~~~~~~~
compilation terminated.

ここによると、次のように説明されている。

If you get an error like this::

/usr/local/go/pkg/tool/linux_amd64/link: running g++ failed: exit status 1
/usr/bin/ld: error: cannot find -lLLVM-7
cgo-gcc-prolog:58: error: undefined reference to 'LLVMVerifyFunction'
cgo-gcc-prolog:80: error: undefined reference to 'LLVMVerifyModule'
[...etc...]
Or like this::

../go-llvm/analysis.go:17:93: fatal error: llvm-c/Analysis.h: No such file or directory
 #include "llvm-c/Analysis.h" // If you are getting an error here read bindings/go/README.txt

It means something is wrong with your LLVM installation. Make sure LLVM 7 is installed (Debian package llvm-7-dev). If it still doesn’t work, you can try running:

cd $GOPATH/src/github.com/tinygo-org/go-llvm
make config
And retry:

go install github.com/tinygo-org/tinygo

llvm-7-devはインストールしてあるので、make configを実行する。

go-llvmのソースの場所が実際は異なっているので次のようにする。

$ cd $GOPATH/src/tinygo.org/x/go-llvm/
$ make config

アドバイス通りに次を実行する。

$ go install github.com/tinygo-org/tinygo

エラー発生(その2)

今度は次のようなエラーが発生した。

# tinygo.org/x/go-llvm
panic: overlapping edits: [8156,8319)->"func() _Ctype_LLVMMetadataRef{ _cgo0 := /*line :297:3*/d.ref; var _cgo1 *_Ctype_char = /*line :298:3*/name; var _cgo2 _Ctype_size_t = _Ctype_size_t(len(t.Name)); var _cgo3 _Ctype_uint64_t = _Ctype_uint64_t(t.SizeInBits); var _cgo4 _Ctype_LLVMDWARFTypeEncoding = _Ctype_LLVMDWARFTypeEncoding(t.Encoding); _cgoCheckPointer(_cgo0); return _Cfunc_LLVMDIBuilderCreateBasicType(_cgo0, _cgo1, _cgo2, _cgo3, _cgo4); }()", [8299,8312)->" /*line :302:3*/_Ctype_LLVMDIFlags /*line :302:16*/"

goroutine 1 [running]:
cmd/internal/edit.(*Buffer).Bytes(0xc0000e1650, 0xc0001b20e0, 0x5f171f, 0xe)
    /usr/local/go/src/cmd/internal/edit/edit.go:79 +0x5a4
main.(*Package).writeOutput(0xc0000b4000, 0xc0000a63c0, 0x7ffe1ab7ad12, 0x35)
    /usr/local/go/src/cmd/cgo/out.go:562 +0x38e
main.main()
    /usr/local/go/src/cmd/cgo/main.go:356 +0xceb

これは解決方法も示されていなかったので、こまった。

どうやらgo-llvmでエラーが発生しているらしい。

解決方法

go-llvmのソースはgitリポジトリなので、コミットログを調べてみた。

commit 7707ae5d1261a8929edea7336c8087ca8b520d8d (HEAD -> master, origin/master, origin/llvm8, origin/HEAD)
Author: Ayke van Laethem <aykevanlaethem@gmail.com>
Date:   Thu Feb 14 11:10:53 2019 +0100

    Switch to LLVM 8

なんと、(現時点の)最新のコミットで LLVM 8に移行した と。

気を取り直してブランチを確認してみる。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/llvm7
  remotes/origin/llvm8
  remotes/origin/make-release
  remotes/origin/master
  remotes/origin/windows

llvm7系のブランチはあるみたい。

tinygoのgo getでリポジトリが汚れているようなので一度きれいにする。

$ git reset --hard HEAD

llvm7ブランチに切り替える

$ git checkout -b llvm7 origin/llvm7

ビルドしてみる。

$ go build -v  github.com/tinygo-org/tinygo
tinygo.org/x/go-llvm
github.com/tinygo-org/tinygo/ir
github.com/tinygo-org/tinygo/interp
github.com/tinygo-org/tinygo/compiler
github.com/tinygo-org/tinygo

通った。

インストール

$ go install github.com/tinygo-org/tinygo

成功した!

まとめ

go-llvmLLVM 8に移行していた。 回避するには手でブランチを切り替える必要がある。

go getでは依存するリポジトリのコミットかブランチを指定できればよいのに。。

(4/7 追記) goのバージョンは1.11系(現時点の最新は1.11.7)をインストールする必要がある。

参考

Yocto gdbserverでリモートデバッグ

はじめに

gdbserverによるリモートデバッグ環境を今まで真面目に作ったことがなかったので調べてみた。

詳細な手順はここで紹介されている。

ターゲットはラズベリーパイ3。

この方法は、ターゲットにインストールされている既存のアプリケーションのデバッグに役立つ。

環境構築

いつものやつ。

INITはsystemdで。

ソース取得

$ mkdir -p rpi-thud/layers
$ cd rpi-thud/layers
$ git clone git://git.yoctoproject.org/poky.git -b thud
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b thud
$ git clone git://git.openembedded.org/meta-openembedded -b thud
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi/

local.confの修正

local.confを次のように修正する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart(optional)
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# debugfs
IMAGE_GEN_DEBUGFS = "1"
IMAGE_FSTYPES_DEBUGFS = "tar.bz2"

# gdbserver
IMAGE_INSTALL_append = " gdbserver"

# debug build
DEBUG_BUILD = "1"

# network 
IMAGE_INSTALL_append = " connman connman-client"
EXTRA_IMAGE_FEATURES += "ssh-server-openssh"

ビルド

$ bitbake core-image-base
$ bitbake core-image-base -c populate_sdk

SDKのインストール

できあがったpoky-glibc-x86_64-core-image-base-cortexa7t2hf-neon-vfpv4-toolchain-2.6.2.shを実行しSDKをインストールする。

$ tmp/deploy/sdk/poky-glibc-x86_64-core-image-base-cortexa7t2hf-neon-vfpv4-toolchain-2.6.2.sh

ターゲットを起動する

できあがったcore-image-base-raspberrypi3.rpi-sdimgをマイクロSDカードに書き込み、ラズベリーパイ3を起動する。

$ sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M

/dev/sdXは環境に応じてsdbsdcに変更する。

debugfsの設定

デバッグに必要なシンボルファイルなどを完全に含んだsysrootを作成するには、debugfsを使用する必要がある。 ここでいうdebugfsカーネル機能のdebugfsとは異なり、デバッグ情報を含んだファイルシステムイメージのことを指す。

debugfsを生成するにはlocal.confに次の行を追記する。

IMAGE_GEN_DEBUGFS = "1"
IMAGE_FSTYPES_DEBUGFS = "tar.bz2"

debugfsはデバッグ情報のみを含んだいわばフラグメントなので、単体で使用しても意味がない。 使用するには次のようにする。

$ mkdir debugfs
$ cd debugfs
$ tar xvf ../build/tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.tar.bz2
$ tar xvf ../build/tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3-dbg.tar.bz2

populate_sdkで作成したツールチェインをインストールした場所にもデバッグ情報を含んだsysrootが存在するが、 ターゲット上のすべてのファイルが含まれているわけではない。 SDKを使用して新規に開発したアプリケーションをデバッグするのには十分だが、それ以外のターゲット上のアプリケーションをデバッグするときには debugfsを含んだ完全なファイルシステムのイメージが必要となる。

デバッグ手順

次のような手順になる。

  1. ターゲットでgdbserverを起動
  2. ホストPCでgdbを起動

試しにlsコマンドをデバッグしてみる。

ターゲット側

$ gdbserver localhost:1234 /bin/ls

ホストPC側

SDKに含まれるgdbを使用する場合は次のようになる。

$ source /opt/poky/2.6.2/environment-setup-cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi
$ cd debugfs/../ #debugfsの1つ上の階層
$ arm-poky-linux-gnueabi-gdb
(gdb) set sysroot debugfs
(gdb) set substitute-path /usr/src/debug debugfs/usr/src/debug
(gdb) target remote ターゲットのIPアドレス:1234

この時点からデバッグ可能となる。

次のようにデバッグする。

(gdb) b ls_main
(gdb) c 

これで(busyboxでの)lsコマンドのエントリポイントでブレークする。

gdbgui

gdbguiを使用する場合は次のようにする。

$ gdbgui -g arm-poky-linux-gnueabi-gdb

デバッグビルドについて

強い最適化がかかっている場合だと、シンボルデバッグ時におかしな挙動に見えることがある。 DEBUG_BUILDを有効化すると、bitbake実行時の最適化を抑えることができる。

local.confに次の行を追加する。

DEBUG_BUILD = "1"

これがない場合はTARGET_CFLAGSは次のようになる。

export TARGET_CFLAGS=" -O2 -pipe -g -feliminate-unused-debug-types -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0=/usr/src/debug/core-image-base/1.0-r0 -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot= -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot-native= "

DEBUG_BUILD= "1"の場合のTARGET_CFLAGSは次のようになる。

export TARGET_CFLAGS=" -O -fno-omit-frame-pointer -g -feliminate-unused-debug-types -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0=/usr/src/debug/core-image-base/1.0-r0 -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot= -fdebug-prefix-map=/home/mickey/work/yocto/rpi-thud/build/tmp/work/raspberrypi3-poky-linux-gnueabi/core-image-base/1.0-r0/recipe-sysroot-native=  -pipe"

-O2-Oになり、-fno-omit-frame-pointerがついている。これによってデバッグ情報が失われないようになっている。

まとめ

SDKを使用して新規に開発したアプリケーションをデバッグするにはSDKに含まれるsysrootのターゲット側のディレクトリで間に合うが、 既存のアプリケーションや、実機環境に近い状態でインテグレートした後のアプリケーションをデバッグする際には、 debugfsを使用する必要があることがわかった。

pandoc + reveal.jsの使い方

はじめに

2年ほど前にインターフェースオフ会の発表資料を作成した時にpandoc + reveal.jsを使ったが、使い方をまとめていなかったので思い出しがてらまとめる。

ただ、この時の発表では会場でノートPCが壊れて起動しないという散々な状況だったので、私としては苦い思い出。

環境の作成

ディレクトリ構成

reveal.jsのディレクトリとスライドのマークダウンは同じフォルダに置いておく。

work
├── input.md
└── reveal.js

イメージがある場合はこんな感じにしておくと便利だと思う。

work
├── input.md
├── images
│   └── imagefile.jpg
└── reveal.js

reveal.jsのダウンロード

$ git clone https://github.com/hakimel/reveal.js.git

スライドの生成

マークダウンを置いたディレクトリでpandocを実行する。

$ pandoc -s -t revealjs -o slide.html input.md 

とりあえず何か作ってみる

次の内容をinput.mdとして作成する。

% スライドのテスト
% Yusuke Mitsuki (発表者)
% 2019/4/1(発表年月日)

# 見出し1

本文本文本文本文本文本文本文本文

## 見出し2

本文本文本文本文本文本文本文本文

### 見出し3

本文本文本文本文本文本文本文本文

作ったものを見る

上記のpandocを実行すると、slide.htmlが作成されている。

これをブラウザで開くとスライドが見れる。

とりあえず、方向キーの左右で進んだり戻ったりできる。

表示効果

マークダウンの構文でいろいろできる。

箇条書きや番号付きリスト、画像の挿入などはそのまま使用できる。

ポーズ

. . .で本文中にポーズを挟むことができる。

まえ

. . .

うしろ

発表者ノート

スライドには表示されない、発表者向けのノートを入れることができる。

<div class="notes">
これはノートです。

- リストのような
- Markdownも含めることができます。
- ノート本文と1行空けて下さい。
</div>

スライド表示中にsを押すと、発表者ノート表示用のポップアップが表示される。 これは現在時刻と開始からの経過時間なども一緒に表示されるので便利。

スライドレベル

--slide-levelオプションを使用すると見出しの階層によってスライドをネストすることができる。

指定した数より上の階層には本文が入れられない(無視される)。例えばスライドレベル2であれば見出し1の下の本文が無視される。

$ pandoc -s -t revealjs -o slide.html input.md --slide-level=2

2以上であれば、縦方向にもスライドするようになる。

個人的には2あれば十分。

テーマの変更

-V theme:テーマで使用するテーマを変更することができる。

テーマ 概要
beige ベージュ色の背景、暗い色の文字、茶色のリンク
black 黒い背景、白い文字、青いリンク (既定のテーマ)
blood 黒い背景、白い文字、赤いリンク
white 白い背景、黒い文字、青いリンク
league 灰色の背景、白い文字、青いリンク (3.0.0以前のreveal.jsの既定のテーマ)
moon 暗い青の背景、ベージュの文字、青いリンク
night 黒い背景、太く白い文字、オレンジ色のリンク
serif カプチーノ色の背景、灰色の文字、茶色のリンク
simple 白い背景、黒い文字、青いリンク
sky 青い背景、細い暗い色の文字、青いリンク
solarized クリーム色の背景、暗い緑の文字、青いリンク

キーバインド

とりあえず次のような事ができる。

キー 機能
f 全画面
o アウトライン表示
n 次のページ
p 前のページ
b 黒画面
s ノートの表示(ポップアップ)
h 左のページ
j 下のページ
k 上のページ
l 右のページ

テーマの追加

テーマを追加したい場合はreveal.js/css/themecssを追加すれば良い。

今回は既存のテーマをベースに新しいテーマmy-theme.cssを作成する。

$ cp reveal.js/css/theme/moon.css reveal.js/css/theme/my-theme.css

次のように変更する。

--- reveal.js/css/theme/moon.css 2019-04-01 13:34:34.249863370 +0900
+++ reveal.js/css/theme/my-theme.css  2019-04-01 15:01:48.082784565 +0900
@@ -20,7 +20,7 @@
 
 .reveal {
   font-family: "Lato", sans-serif;
-  font-size: 40px;
+  font-size: 36px;
   font-weight: normal;
   color: #93a1a1; }
 
@@ -54,18 +54,18 @@
   font-weight: normal;
   line-height: 1.2;
   letter-spacing: normal;
-  text-transform: uppercase;
+  text-transform: none;
   text-shadow: none;
   word-wrap: break-word; }
 
 .reveal h1 {
-  font-size: 3.77em; }
+  font-size: 2.5em; }
 
 .reveal h2 {
-  font-size: 2.11em; }
+  font-size: 1.6em; }
 
 .reveal h3 {
-  font-size: 1.55em; }
+  font-size: 1.3em; }
 
 .reveal h4 {
   font-size: 1em; }

ベースのテーマのフォントサイズが気に入らない場合などはこのようにすると便利。 もちろん全く新規にテーマを作成しても良い。

pandocでは次のようにテーマを指定する。

$ pandoc -V theme:my-theme -s -t revealjs -o slide.html input.md --slide-level=2

pdf出力及び印刷

ブラウザのアドレスバーにあるスライドのURLに?print-pdfを追加する。

その後、ブラウザの印刷機能で印刷する。

PDF出力はブラウザがpdf出力できる場合はそれで行なう。

まとめ

pandocとreveal.jsを使用すると、スライドの作成が簡単になる。

おまけ

機能確認用に作ったinput.md

% スライドのテスト
% Yusuke Mitsuki (発表者)
% 2019/4/1(発表年月日)

# 見出し1

本文本文本文本文本文本文本文本文

<div class="notes">
これはノートです。
</div>

## 見出し2

本文本文本文本文本文本文本文本文

<div class="notes">
スライドには表示されません。
- 箇条書き
- もできます。
</div>


### 見出し3

本文本文本文本文本文本文本文本文

<div class="notes">
ノート画面は意外と便利
</div>



# 画面効果

## リスト

- item1
- item2
- item3

## リスト(*)

* item1
* item2
* item3

## 番号付きリスト

1. item1
1. item2
4. item3
4. item4

## ポーズ

まえ

. . .

うしろ

## イメージ


![サンプル画像](https://cdn-ak.f.st-hatena.com/images/fotolife/m/mickey_happygolucky/20190328/20190328115103.png)

## テーブル

 キー       機能
--------- ---------------------------------------
 f         全画面
 o         アウトライン表示
 n         次のページ
 p         前のページ
 b         黒画面


5行程度が限界かも。

参考

GNU Global(gtags)の対応言語を増やしてみる

はじめに

最近、仕事でgoのコードを読む必要が出てきた。

コードを読む時はemacs+gtagsを使用しているが、デフォルトではgoは対応していない。

ここPygmentsexuberant-ctagsを使ってgtagsの対応言語を増やす方法が紹介されてたので試してみる。

作業環境

作業環境はUbuntu 18.04。

Pygmentsのインストール

Pygmentsはpythonのパッケージで、pipでインストールできる。

$ sudo pip install Pygments

exuberant-ctagsのインストール

Pygmentsの効果を最大限に活かすためにはctagsが必要なのでインストールする。

$ sudo apt-get install exuberant-ctags

aptのgtagsをアンインストール

インストールしたpythonパッケージとの連携を有効にするには自分でビルドする必要があるので、 Ubuntu標準のパッケージでGNU Global(gtags)をインストールしている場合はアンインストールする。

$ sudo apt purge global

GNU Global(gtags)をビルド

次のコマンドでソースをダウンロードしてconfigureスクリプトを実行する。

$ wget http://tamacom.com/global/global-6.6.3.tar.gz
$ tar xvf global-6.6.3.tar.gz
$ cd global-6.6.3
$ ./configure

exuberant-ctagsが有効になっている場合は./configureで次の行が表示されている。

checking for exuberant ctags program... using /usr/bin/ctags

Pygmentsが有効になっている場合は./configureで次の行が表示されている。

config.status: creating plugin-factory/Makefile
config.status: creating plugin-factory/pygments_parser.py

次にmakeを実行してビルド、インストールする。

$ make -j $(nproc)
$ sudo make install

設定ファイル

設定ファイルを/etcの下にコピーする。

$ sudo cp /usr/local/share/gtags/gtags.conf /etc/

実行

$ gtags --gtagslabel=pygments

もしくは環境変数に設定する。

$ export GTAGSLABEL=pygments
$ gtags

まとめ

設定ファイルのコピーを忘れるとgtagsコマンド実行時にタグファイルは作成されるが、 ほぼジャンプできないので注意が必要。

go意外にも対応言語が増えるので、普段からGNU Globalを使用している人にとっては便利。

参考