List of Testing - text.Baldanders.info
tag:text.Baldanders.info,2018-03-26:/tags
2018-03-26T19:27:50+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
テスト・フレームワークで Project Euler を解く
tag:text.Baldanders.info,2018-03-26:/golang/testing-for-project-euler/
2018-03-26T10:27:50+00:00
2021-08-12T21:22:05+00:00
ベンチマーク・テストもできるよ。
Spiegel
https://baldanders.info/profile/
<p>別のセクションで「<a href="https://text.baldanders.info/remark/2018/03/project-euler/">Project Euler で遊ぶ</a>」話を書いたのだが,この記事のコメントで「ユニットテストを書く練習になる」というのがあって「なるほど!」と膝を打った。
つか,自力で気づけよ,自分。
まだまだ TDD が身につかないなぁ。</p>
<p>ちうわけで,以下の問題(Problem)をテスト・フレームワークで解いてみる。</p>
<figure lang="en">
<blockquote>
<q>If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.<br>
Find the sum of all the multiples of 3 or 5 below 1000.</q>
</blockquote>
<figcaption><div>via <q><a href="https://projecteuler.net/problem=1">Problem 1 - Project Euler</a></q></div></figcaption>
</figure>
<figure>
<blockquote>
<q>10未満の自然数のうち, 3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり, これらの合計は 23 になる.<br>
同じようにして, 1000 未満の 3 か 5 の倍数になっている数字の合計を求めよ.</q>
</blockquote>
<figcaption><div><q><a href="http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%201">Problem 1 - PukiWiki</a></q>より</div></figcaption>
</figure>
<p>まず,ベタに解いたコード。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Answer0 returns answer to this problem
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">max</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">max</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">n</span><span class="o">%</span><span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="p">(</span><span class="nx">n</span><span class="o">%</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">sum</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これをテストにかける。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">problem1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>実行すると以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v .
</span></span><span class="line"><span class="cl">=== RUN TestAnswer
</span></span><span class="line"><span class="cl">--- PASS: TestAnswer (0.00s)
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok project-euler/problem-1 0.128s
</span></span></code></pre></div><p>これで最初の例示について正しい答えを出していることがわかる。
では1,000未満についてもやってみようか。
以下のようにテスト項目を追加する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span> <span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>まだ正解が分からないので,とりあえず <code>0</code> をぶちこんでいる。
これを実行する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v .
</span></span><span class="line"><span class="cl">=== RUN TestAnswer
</span></span><span class="line"><span class="cl">--- FAIL: TestAnswer (0.00s)
</span></span><span class="line"><span class="cl"> answer_test.go:16: Answer0(1000) = ******, want 0.
</span></span><span class="line"><span class="cl">FAIL
</span></span><span class="line"><span class="cl">FAIL project-euler/problem-1 0.106s
</span></span><span class="line"><span class="cl">exit status 1
</span></span></code></pre></div><p>テストは当然失敗するが,回答が表示されるので<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> これを <a href="https://projecteuler.net/">Project Euler</a> のサイトで入力してチェックする。
正解が分かったら改めて正解をテストにセットしなおす。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span> <span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="o">******</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>これで再テストすると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v .
</span></span><span class="line"><span class="cl">=== RUN TestAnswer
</span></span><span class="line"><span class="cl">--- PASS: TestAnswer (0.00s)
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok project-euler/problem-1 0.128s
</span></span></code></pre></div><p>ちゃんと PASS していることが確認できた。</p>
<h2>改良版</h2>
<p>ここで解説を参考に改良版のコードを組む。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Answer1 returns answer to this problem (refactoring version)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Answer1</span><span class="p">(</span><span class="nx">max</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="o">-</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="mi">3</span><span class="o">*</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span> <span class="o">:=</span> <span class="p">(</span><span class="nx">max</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">n</span> <span class="o">*</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span> <span class="p">(</span><span class="nx">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>このコードも同じテストにかける。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span> <span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="o">******</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">a</span> <span class="p">=</span> <span class="nf">Answer1</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer1(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>全てのテストケースでパスすればOK。</p>
<h2>ベンチマーク・テスト</h2>
<p>更に <code>Answer0()</code>, <code>Answer1()</code> 両関数をベンチマーク・テストで比較してみることにする。
ベンチマーク・テスト用のコードは以下の通り<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAnswer0</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAnswer1</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nf">Answer1</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ではベンチマーク・テストを実行してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench . -benchmem
</span></span><span class="line"><span class="cl">goos: windows
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: project-euler/problem-1
</span></span><span class="line"><span class="cl">BenchmarkAnswer0-4 500000 2415 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkAnswer1-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok project-euler/problem-1 1.952s
</span></span></code></pre></div><p>おおっ。
万倍ちがうぜ(笑)</p>
<p>このようにテストフレームワークを使って楽しく <a href="https://projecteuler.net/">Project Euler</a> で遊ぶことができる。
テストを書く習慣づけになればもっといいんだろうけど。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/golang/testing/">Go 言語のテスト・フレームワーク</a></li>
<li><a href="https://text.baldanders.info/golang/join-strings/">文字列連結はどれが速い?</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>一応ネタバレ防止用に伏字にしている。ご容赦。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>ベンチマーク用のコードは <code>***_test.go</code> ファイルに含めること。また関数名は <code>Benchmark</code> から始まる名前にする。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Cobra の使い方とテスト
tag:text.Baldanders.info,2017-12-06:/golang/using-and-testing-cobra/
2017-12-06T12:01:33+00:00
2021-08-12T21:22:05+00:00
spf13/cobra そのものについてちゃんと書いてない気がするので,今回はコードの書き方からテストまでをひと通り紹介していく。
Spiegel
https://baldanders.info/profile/
<p>だいぶ <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> の扱いに慣れてきたので,そろそろブログにまとめておこうかなと。
以前に CLI (Command-Line Interface) とファサード・パターンについては以下の記事に</p>
<ul>
<li><a href="https://text.baldanders.info/golang/cli-and-facade-pattern/">コマンドライン・インタフェースとファサード・パターン</a></li>
</ul>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> の使い方については,触りの部分を以下の記事に書いた。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a></li>
<li><a href="https://text.baldanders.info/golang/codic-api/">Codic API を利用するパッケージを作ってみた</a> (<a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> との連携について)</li>
</ul>
<p>ただ <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> そのものについてちゃんと書いてない気がするので,今回はコードの書き方からテストまでをひと通り紹介していく。
漫然と紹介するのもアレなので,今回は以下の CLI を作ることを目標にする。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cli-demo show -i 123 -s 文字列 -b
</span></span><span class="line"><span class="cl">Integer option value: 123
</span></span><span class="line"><span class="cl"> String option value: 文字列
</span></span><span class="line"><span class="cl">Boolean option value: true
</span></span></code></pre></div><p>なお,この記事の作業の結果は以下のリポジトリに置いてある。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/cli-demo">spiegel-im-spiegel/cli-demo: Demonstration for Command Line Interface</a></li>
</ul>
<h2><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> のインストール</h2>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> には CLI ツールもあるのだがバイナリは提供されていないので,自前で取ってきてコンパイルする。
といっても</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go get -u github.com/spf13/cobra/cobra
</span></span></code></pre></div><p>とするだけだが。
<code>github.com/spf13/cobra</code> で get しようとすると CLI のコンパイルを行わないので注意が必要である。
CLI の Usage はこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra -h
</span></span><span class="line"><span class="cl">Cobra is a CLI library for Go that empowers applications.
</span></span><span class="line"><span class="cl">This application is a tool to generate the needed files
</span></span><span class="line"><span class="cl">to quickly create a Cobra application.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cobra [command]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Available Commands:
</span></span><span class="line"><span class="cl"> add Add a command to a Cobra Application
</span></span><span class="line"><span class="cl"> help Help about any command
</span></span><span class="line"><span class="cl"> init Initialize a Cobra Application
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -a, --author string author name for copyright attribution (default "YOUR NAME")
</span></span><span class="line"><span class="cl"> --config string config file (default is $HOME/.cobra.yaml)
</span></span><span class="line"><span class="cl"> -h, --help help for cobra
</span></span><span class="line"><span class="cl"> -l, --license string name of license for the project
</span></span><span class="line"><span class="cl"> --viper use Viper for configuration (default true)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Use "cobra [command] --help" for more information about a command.
</span></span></code></pre></div><h2>初期化処理</h2>
<p>まずは <code>init</code> サブコマンドで雛形となるソースコードを展開する。
ソースを展開したいフォルダで</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">cobra --viper=false init .
</span></span></code></pre></div><p>とする。
今回は <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> は使わないので <code>--viper</code> オプションは無効にしておく。</p>
<p>これで対象のフォルダに3つのファイルが出力される</p>
<ul>
<li><code>main.go</code></li>
<li><code>LICENSE</code></li>
<li><code>cmd/root.go</code></li>
</ul>
<p><code>LICENSE</code> はライセンス・ファイルなのでそのまま利用するなり他のファイルに差し替えるなりすればいい。
<code>main.go</code> と <code>cmd/root.go</code> が実際の雛形になるのだが,やたらコメントが多いので,整理したものを以下に示す。</p>
<p>まずは <code>main.go</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"github.com/spiegel-im-spiegel/cli-demo/cmd"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>次に <code>cmd/root.go</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// RootCmd represents the base command when called without any subcommands
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">RootCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"cli-demo"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Run: func(cmd *cobra.Command, args []string) { },
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Execute is called from main function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">RootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>呼び出し関係は以下の通り。</p>
<figure style='margin:0 auto;text-align:center;'>
<div class="mermaid">
graph LR
main["main()"]
exec1["cmd.Execute()"]
exec2["cmd.RootCmd.Execute()"]
main-->exec1
exec1-->exec2
</div></figure>
<p>もちろん <code>main()</code> 関数から直接 <code>cmd.RootCmd.Execute()</code> 関数を呼んでも構わない(このままであれば)。
この状態でコマンドを起動すると以下のようになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go -h
</span></span><span class="line"><span class="cl">Long comment
</span></span></code></pre></div><p>まぁ,当然だよね。
現状での問題は3つある。</p>
<ol>
<li><code>cmd.RootCmd</code> を他パッケージから直接操作できてしまう</li>
<li>引数と標準入出力を <code>cmd.RootCmd</code> が握っていてコントロールできない(ように見える)</li>
<li><code>cmd.RootCmd.Execute()</code> 関数がエラー時に <code>os.Exit()</code> 関数で強制終了してしまう</li>
</ol>
<p>特に2番目が致命的。
何故ならこのままではテストができないからだ。
そこで <code>cmd.Execute()</code> 関数を改造することにする。</p>
<h2><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> の導入</h2>
<p>その前に標準入出力の取り回しを簡単にするために,手前味噌で申し訳ないが, <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> を導入する。
これを使えば標準入出力をひとつのインスタンスで取り回しできる<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">)</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Hello world"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">run</span><span class="p">(</span><span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)).</span><span class="nf">Exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>rwi.New()</code> の初期化は Functional Options パターンを使っている。
Functional Options パターンについて詳しくは以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/functional-options-pattern/">インスタンスの生成と Functional Options パターン</a></li>
</ul>
<h2>cmd.Execute() 関数を改造する</h2>
<p>では <code>cmd.Execute()</code> 関数の改造を行おう。
途中を省いて結果は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="c1">//CUI instance
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Execute is called from main function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">exit</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//panic hundling
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">"Panic:"</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">depth</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="nx">depth</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pc</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="nx">line</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">Caller</span><span class="p">(</span><span class="nx">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">" ->"</span><span class="p">,</span> <span class="nx">depth</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">FuncForPC</span><span class="p">(</span><span class="nx">pc</span><span class="p">).</span><span class="nf">Name</span><span class="p">(),</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//execution
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">ui</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetArgs</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetOutput</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nf">ErrorWriter</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>前半は <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">panic</a> 時のハンドリングで,スタック追跡できるようにしている。
<a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">panic</a> 時のハンドリングについて詳しくは以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/stack-trace-and-panic-handling/">スタック追跡とパニック・ハンドリング</a></li>
</ul>
<p>後半では <code>cmd.rootCmd</code> に引数と標準出力をセットしてから <code>cmd.rootCmd.Execute()</code> 関数を起動している<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。
エラー時は <code>os.Exit()</code> 関数で強制終了するのではなく,ちゃんとステータスを返すようにした。</p>
<p>そうそう。
<code>cmd.RootCmd</code> ではパッケージ外から直接操作できてしまうので <code>cmd.rootCmd</code> と小文字にしている。
小さなことからコツコツと(笑)</p>
<p>これで <code>main()</code> 関数もこのように書き換えることができる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span>
</span></span><span class="line"><span class="cl"> <span class="p">).</span><span class="nf">Exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2>サブコマンドの追加</h2>
<p>ではいよいよサブコマンド <code>show</code> を追加する。
サブコマンドの追加は <code>main.go</code> のあるフォルダで以下のコマンドを実行する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra add show
</span></span></code></pre></div><p>これにより <code>cmd/show.go</code> ファイルが追加された。
これも中身はコメントが山ほどあるので,整理したものを以下に示す。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// showCmd represents the show command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">showCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"show"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Run</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"show called"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nx">showCmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>この状態で実際に動かしてみるとこうなる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go -h
</span></span><span class="line"><span class="cl">Long comment
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cli-demo [flags]
</span></span><span class="line"><span class="cl"> cli-demo [command]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Available Commands:
</span></span><span class="line"><span class="cl"> help Help about any command
</span></span><span class="line"><span class="cl"> show Short comment for show sub-command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -h, --help help for cli-demo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Use "cli-demo [command] --help" for more information about a command.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go show -h
</span></span><span class="line"><span class="cl">Long comment for show sub-command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cli-demo show [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -h, --help help for show
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go show
</span></span><span class="line"><span class="cl">show called
</span></span></code></pre></div><h3>オプションの追加</h3>
<p>このコードに対してオプションを追加していく。
こんな感じでいいだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// showCmd represents the show command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">showCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"show"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RunE</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetInt</span><span class="p">(</span><span class="s">"integer"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetBool</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetString</span><span class="p">(</span><span class="s">"string"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Integer option value:"</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">" String option value:"</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Boolean option value:"</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nx">showCmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">IntP</span><span class="p">(</span><span class="s">"integer"</span><span class="p">,</span> <span class="s">"i"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"integer option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">BoolP</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">"boolean option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">StringP</span><span class="p">(</span><span class="s">"string"</span><span class="p">,</span> <span class="s">"s"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"string option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>cmd.showCmd</code> 内の <code>Run</code> が <code>RunE</code> に変わってる点に注目。
これにより,関数内で error が発生した場合に,それを返り値で渡すことができる。</p>
<p>試しにちょろんと動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go show -h
</span></span><span class="line"><span class="cl">Long comment for show sub-command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cli-demo show [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -b, --boolean boolean option
</span></span><span class="line"><span class="cl"> -h, --help help for show
</span></span><span class="line"><span class="cl"> -i, --integer int integer option
</span></span><span class="line"><span class="cl"> -s, --string string string option
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go show -i 123 -s 日本語 -b
</span></span><span class="line"><span class="cl">Integer option value: 123
</span></span><span class="line"><span class="cl"> String option value: 日本語
</span></span><span class="line"><span class="cl">Boolean option value: true
</span></span></code></pre></div><p>おおっ。
なんか動いてるような気がする。</p>
<p>じゃあテストを始めようか。</p>
<h2>cobra.Command を返す関数を作る。</h2>
<p>と言いたいところだけど,このままではまだテストができない。
何故かというと, <code>cmd.rootCmd</code> も <code>cmd.showCmd</code> も static な変数として定義されているので,そのままテストを繰り返すと前回の状態が残ってしまって正しいテストにならないからだ。</p>
<p>じゃあどうすればいいかというと, <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>cobra</code></a><code>.Command</code> を返す関数を作って,その中で <code>cmd.rootCmd</code> や <code>cmd.showCmd</code> に相当するインスタンスを作ればいいのである。</p>
<p>じゃあ,まずは <code>cmd/root.go</code> の完全版から。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"runtime"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="c1">//CUI instance
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//newRootCmd returns cobra.Command instance for root command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">newRootCmd</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">ui</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"cli-demo"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RunE</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"no command"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetArgs</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetOutput</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nf">ErrorWriter</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nf">newShowCmd</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">rootCmd</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Execute is called from main function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">exit</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//panic hundling
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">"Panic:"</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">depth</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="nx">depth</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pc</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="nx">line</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">Caller</span><span class="p">(</span><span class="nx">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">" ->"</span><span class="p">,</span> <span class="nx">depth</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">FuncForPC</span><span class="p">(</span><span class="nx">pc</span><span class="p">).</span><span class="nf">Name</span><span class="p">(),</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//execution
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">newRootCmd</span><span class="p">(</span><span class="nx">ui</span><span class="p">,</span> <span class="nx">args</span><span class="p">).</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>次は <code>cmd/show.go</code> の完全版。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//newShowCmd returns cobra.Command instance for show sub-command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">newShowCmd</span><span class="p">()</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"show"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RunE</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetInt</span><span class="p">(</span><span class="s">"integer"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetBool</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetString</span><span class="p">(</span><span class="s">"string"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Integer option value:"</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">" String option value:"</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Boolean option value:"</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">IntP</span><span class="p">(</span><span class="s">"integer"</span><span class="p">,</span> <span class="s">"i"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"integer option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">BoolP</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">"boolean option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">StringP</span><span class="p">(</span><span class="s">"string"</span><span class="p">,</span> <span class="s">"s"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"string option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">showCmd</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>なんで <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> が最初からこういう雛形を作ってくれないかというと,おそらく <code>cobra add</code> コマンドで <code>cmd/root.go</code> を触るわけにはいかないため(たぶん既にユーザが触ってる), static 変数にして <code>init()</code> 関数でコマンドを繋げるような組み方しかできなかったんだと思う。
でも手作業でこういう雛形を作ってしまえば以後はコピペでいくらでも量産できるので,テストのことを考えれば,面倒でも最初に手間を掛けたほうがいいかもしれない。</p>
<h2>テストを書く</h2>
<p>ようやくテストが書けるよ。
とりあえず正常系のみ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bytes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestShowNormal</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">want</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">,</span> <span class="s">"-i"</span><span class="p">,</span> <span class="s">"123"</span><span class="p">,</span> <span class="s">"-s"</span><span class="p">,</span> <span class="s">"日本語"</span><span class="p">,</span> <span class="s">"-b"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 123\n String option value: 日本語\nBoolean option value: true\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">,</span> <span class="s">"-i"</span><span class="p">,</span> <span class="s">"123"</span><span class="p">,</span> <span class="s">"-s"</span><span class="p">,</span> <span class="s">"日本語"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 123\n String option value: 日本語\nBoolean option value: false\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">,</span> <span class="s">"-i"</span><span class="p">,</span> <span class="s">"123"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 123\n String option value: \nBoolean option value: false\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 0\n String option value: \nBoolean option value: false\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">out</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errOut</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ui</span> <span class="o">:=</span> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">out</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">errOut</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="o">:=</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">ui</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">exit</span> <span class="o">!=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Execute() err = \"%v\", want \"%v\"."</span><span class="p">,</span> <span class="nx">exit</span><span class="p">,</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">out</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">want</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Execute() Stdout = \"%v\", want \"%v\"."</span><span class="p">,</span> <span class="nx">out</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="nx">c</span><span class="p">.</span><span class="nx">want</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">errOut</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Execute() Stderr = \"%v\", want \"%v\"."</span><span class="p">,</span> <span class="nx">errOut</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="s">""</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>標準出力と標準エラー出力を <a href="https://golang.org/pkg/bytes/" title="bytes - The Go Programming Language"><code>bytes</code></a><code>.Buffer</code> で代替えしているのがお分かりだろうか。
これなら CLI でもかなりの部分をテストでカバーできる。
ここには挙げていないが,当然パイプのテストも可能である。</p>
<h2><a href="https://github.com/golang/dep" title="golang/dep: Go dependency management tool">dep</a> で <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> を管理する</h2>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> をはじめとする外部パッケージを <a href="https://github.com/golang/dep" title="golang/dep: Go dependency management tool">dep</a> を使って <code>vendor/</code> 配下に置くのなら, <code>Gopkg.toml</code> の記述は以下でいいだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.0.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.7.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.8.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">prune</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">go-tests</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nx">unused-packages</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>ちなみに今回のデモのパッケージ依存関係はこんな感じになっている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep status -dot | dot -Tpng -o dependency.png
</span></span></code></pre></div><figure style='margin:0 auto;text-align:center;'><a href="dependency.png"><img src="dependency.png" srcset="dependency.png 754w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://qiita.com/tkit/items/3cdeafcde2bd98612428">Golangのコマンドライブラリcobraを使って少しうまく実装する - Qiita</a> : <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>cobra</code></a><code>.Command</code> の関数化のアイデアはこちらからいただいた。感謝</li>
<li><a href="https://qiita.com/izumin5210/items/b06a81002a6934c05185">cobra / pflags でフラグをパースせずに args に残す - Qiita</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51jif840ScL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">改訂2版 みんなのGo言語</a></dt>
<dd>松木 雅幸 (著), mattn (著), 藤原 俊一郎 (著), 中島 大一 (著), 上田 拓也 (著), 牧 大輔 (著), 鈴木 健太 (著)</dd>
<dd>技術評論社 2019-08-01 (Release 2019-08-01)</dd>
<dd>Kindle版</dd>
<dd>B07VPSXF6N (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">改訂2版の目玉は7章の「データベースの扱い方」が追加されたことだろう。他の章では,大まかな構成は1版と同じだが細かい部分が変わっていて Go 1.12 への言及まであるのには驚いた。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2019-08-12">2019-08-12</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 改訂2版 みんなのGo言語 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>コードを見れば分かると思うが <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> には最小限のコードしかないので,たとえば複数の <a href="http://golang.org/ref/spec#Go_statements">goroutine</a> が動いている状態で単一の入出力インスタンスを取り回すとか考えたくもない。なので,取り扱いには十分注意すること。その代わり <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> では <a href="https://creativecommons.org/publicdomain/zero/1.0/" title="Creative Commons — CC0 1.0 Universal">CC0</a> つまり著作者人格権も含めて完全に権利を放棄しているので,自由に使っていただいて構わない。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>cobra</code></a><code>.Command</code> には出力先がひとつしかなく何故か標準出力と標準エラー出力を区別していない。そこで <code>cmd.rootCmd</code> には標準エラー出力をセットして標準出力へのアクセスは <code>cui</code> を <code>cmd</code> パッケージ内のどのメソッドからも参照できるようにした(つまり,少なくとも <code>cmd</code> パッケージ内は単一の <a href="http://golang.org/ref/spec#Go_statements">goroutine</a> で動くことが前提)。 <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> 側が標準出力と標準エラー出力をちゃんと区別してくれれば,もう少しスマートにできるんだけどねぇ。(あと標準入力用の Reader も付けて欲しい。まぁなくても何とかなってるけど<code>w</code>) <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Glide から Dep への移行を検討する
tag:text.Baldanders.info,2017-10-10:/golang/consider-switching-from-glide-to-dep/
2017-10-10T09:02:56+00:00
2020-01-03T06:05:57+00:00
つまり「依存関係(Vendoring)管理ツールとしては dep を推奨するけど移行できない人のために当面はサポートを続けるよ(でも将来は分からん)」という解釈でいいのだろうか。
Spiegel
https://baldanders.info/profile/
<p>(この記事は <a href="https://qiita.com/spiegel-im-spiegel/items/e931ad1a7565d02d179e">Qiita とのマルチポスト</a>です。
まぁ,向こうは草稿版だけど)</p>
<p>久しぶりに <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> を使おうと最新版(<a href="https://github.com/Masterminds/glide/releases/tag/v0.13.0" title="Release 0.13.0 · Masterminds/glide">0.13.0</a>)を見に行ったら “<strong>Consider switching to <a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a></strong>” とか書いてあるじゃない。</p>
<figure lang="en">
<blockquote>
<q>Glide is used by a great number of projects and will continue to get support for some time.
But, the near future is likely in dep.
dep can handle importing Glide config files.
Please consider trying dep on your project or converting to dep.</q>
</blockquote>
<figcaption><div>via <q><a href="https://github.com/Masterminds/glide/releases/tag/v0.13.0">Release 0.13.0</a></q></div></figcaption>
</figure>
<p>まじすか。</p>
<p>つまり「依存関係(Vendoring)管理ツールとしては <a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> を推奨するけど移行できない人のために当面はサポートを続けるよ(でも将来は分からん)」という解釈でいいのだろうか。</p>
<p><a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> は <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>開発プロジェクトの公式ツールで,2017年の始めくらいに日本でも話題になったような気がするが,私は <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> で完全に満足していたのでスルーしていた。
こんなことになるなんて。
ならもう <a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> に移行するしかないぢゃん。</p>
<p>とはいえ,いきなり本番環境に投入するのは怖いので,なにか適当なテストケースはないか,と自分のリポジトリを漁ってたら丁度いいのがあったよ。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/pi">spiegel-im-spiegel/pi: Estimate of Pi with Monte Carlo method.</a></li>
</ul>
<p>これって<a href="https://text.baldanders.info/golang/estimate-of-pi/">モンテカルロ法で遊んでた</a>ときに作ったものだ。
最悪ぶっ壊れてもいいので,これ使って試してみるか。</p>
<h2><a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> の取得</h2>
<p>まず <a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> の取得から始めないとだが,リポジトリ自体は <code>go get</code> コマンドで取得できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go get -u github.com/golang/dep/cmd/dep
</span></span></code></pre></div><p>これをこのまま使ってもいいのだが,<a href="https://github.com/golang/dep/releases" title="Releases · golang/dep">リリースページ</a>にビルド済みのモジュールが置かれているので,ありがたくこれを使わせてもらおう。</p>
<p>最新版(現時点で <a href="https://github.com/golang/dep/releases/tag/v0.4.1" title="Release v0.4.1 · golang/dep">v0.4.1</a>)には Windows 用のモジュール <code>dep-windows-amd64.exe</code> もある。
これを <code>dep.exe</code> にリネームして使う。</p>
<p>万が一があっては困るのでモジュールの SHA256 ハッシュ値を確認しておく(こういうのこそ OpenPGP を使ってくれないものか)。
Windows ユーザで Windows 8.1 以降であれば PowerShell(4.0 以上)で <a href="http://technet.microsoft.com/en-us/library/dn520872.aspx"><code>Get-FileHash</code></a> コマンドレットが使える<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">PS </span><span class="n">C:</span><span class="p">\</span><span class="n">Users</span><span class="p">\</span><span class="n">username</span><span class="p">\</span><span class="n">Downloads</span><span class="p">></span> <span class="nb">Get-FileHash</span> <span class="nb">dep-windows</span><span class="n">-amd64</span><span class="p">.</span><span class="py">exe</span> <span class="n">-Algorithm</span> <span class="n">SHA256</span> <span class="p">|</span> <span class="nb">Format-List</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Algorithm</span> <span class="err">:</span> <span class="n">SHA256</span>
</span></span><span class="line"><span class="cl"><span class="n">Hash</span> <span class="err">:</span> <span class="n">F6E6A872C54D5AE7536AC71FD5BCAC9F4E7B8A1DAFA1EF7C23866E2F3069FE4E</span>
</span></span><span class="line"><span class="cl"><span class="n">Path</span> <span class="err">:</span> <span class="n">C:</span><span class="p">\</span><span class="n">Users</span><span class="p">\</span><span class="n">username</span><span class="p">\</span><span class="n">Downloads</span><span class="p">\</span><span class="nb">dep-windows</span><span class="n">-amd64</span><span class="p">.</span><span class="py">exe</span>
</span></span></code></pre></div><p>これを <code>dep-windows-amd64.exe.sha256</code> に記載されている値と比較する。
改竄されてなければ同じ値になるはずである。
目視は辛いのでテキストエディタ等の検索機能を使えばいいだろう。</p>
<div class="box"><strong>【2017-10-31 追記】</strong> だんだん面倒になってきたので<a href="https://github.com/spiegel-im-spiegel/hash" title="spiegel-im-spiegel/hash: Calculating Hash Value">ハッシュ値を計算するツール</a>を作った。
詳しくは「<a href="https://text.baldanders.info/golang/calculating-hash-value/">Hash 値を計算するパッケージを作ってみた</a>」を参照のこと。</div>
<!--
Windows ユーザには(`sha256sum` といった)標準ツールがないのが痛いのだが, [7-Zip] があるなら,これを使ってハッシュ値を確認できる。
```text
$ 7z.exe h -scrcSHA256 dep-windows-amd64
7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04
Scanning
1 file, 7696896 bytes (7517 KiB)
SHA256 Size Name
---------------------------------------------------------------- ------------- ------------
D4BF3EC10B1808CAB883C6AB2901C396CF463E684FDA350199E93E31806C194A 7696896 dep-windows-amd64
---------------------------------------------------------------- ------------- ------------
D4BF3EC10B1808CAB883C6AB2901C396CF463E684FDA350199E93E31806C194A 7696896
Size: 7696896
SHA256 for data: D4BF3EC10B1808CAB883C6AB2901C396CF463E684FDA350199E93E31806C194A
Everything is Ok
```
これを `dep-windows-amd64.sha256` に記載されている値と比較する。
改竄されてなければ同じ値になるはずである。
目視は辛いのでテキストエディタ等の検索機能を使えばいいだろう。
<div class="box"><p><a href="http://www.nyaos.org/index.cgi?p=NYAGOS" title="NYAOS.ORG - NYAGOS">NYAGOS</a> を使っている人なら <code>.nyagos</code> ファイルに</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">alias</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">sha1sum</span><span class="o">=</span><span class="s1">'%COMSPEC% /c </span><span class="se">\"</span><span class="s1">%PROGRAMFILES%/7-Zip/7z.exe</span><span class="se">\"</span><span class="s1"> h -scrcSHA1 $*'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">sha256sum</span><span class="o">=</span><span class="s1">'%COMSPEC% /c </span><span class="se">\"</span><span class="s1">%PROGRAMFILES%/7-Zip/7z.exe</span><span class="se">\"</span><span class="s1"> h -scrcSHA256 $*'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>とか記述しておけば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ sha256sum dep-windows-amd64
</span></span></code></pre></div><p>で同じ結果が得られる。
改竄の有無を確認するためにファイルのハッシュ値を調べることはよくあるので準備しておくとよい。</p>
</div>
-->
<p>実行モジュールの動作確認もしておく。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep
</span></span><span class="line"><span class="cl">Dep is a tool for managing dependencies for Go projects
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage: "dep [command]"
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Commands:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> init Set up a new Go project, or migrate an existing one
</span></span><span class="line"><span class="cl"> status Report the status of the project's dependencies
</span></span><span class="line"><span class="cl"> ensure Ensure a dependency is safely vendored in the project
</span></span><span class="line"><span class="cl"> prune Pruning is now performed automatically by dep ensure.
</span></span><span class="line"><span class="cl"> version Show the dep version information
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Examples:
</span></span><span class="line"><span class="cl"> dep init set up a new project
</span></span><span class="line"><span class="cl"> dep ensure install the project's dependencies
</span></span><span class="line"><span class="cl"> dep ensure -update update the locked versions of all dependencies
</span></span><span class="line"><span class="cl"> dep ensure -add github.com/pkg/errors add a dependency to the project
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Use "dep help [command]" for more information about a command.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ dep version
</span></span><span class="line"><span class="cl">dep:
</span></span><span class="line"><span class="cl"> version : v0.4.1
</span></span><span class="line"><span class="cl"> build date : 2018-01-24
</span></span><span class="line"><span class="cl"> git hash : 37d9ea0a
</span></span><span class="line"><span class="cl"> go version : go1.9.1
</span></span><span class="line"><span class="cl"> go compiler : gc
</span></span><span class="line"><span class="cl"> platform : windows/amd64
</span></span></code></pre></div><h2><a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> から <a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> への移行</h2>
<p>お試し用の <a href="https://github.com/spiegel-im-spiegel/pi" title="spiegel-im-spiegel/pi: Estimate of Pi with Monte Carlo method.">spiegel-im-spiegel/pi</a> をビルド可能な適当な場所に置く。</p>
<p>このパッケージの <code>glide.yaml</code> はこんな感じになっている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spiegel-im-spiegel/pi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">import</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spiegel-im-spiegel/gocli</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spf13/cobra</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/pkg/errors</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/seehuhn/mt19937</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/davidminor/gorand</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/davidminor/uint128</span><span class="w">
</span></span></span></code></pre></div><p>また <code>glide.lock</code> はこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">hash</span><span class="p">:</span><span class="w"> </span><span class="l">d570123d6231810c51dd17e415673df221fb2dec7ef6ab45cd34093002a87cbb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">updated</span><span class="p">:</span><span class="w"> </span><span class="ld">2016-11-16T17:28:38.2997832</span><span class="m">+09</span><span class="p">:</span><span class="m">00</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">imports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/davidminor/gorand</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">189780b8053a44a111339a4248394fd844c1da40</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">subpackages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">lcg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/davidminor/uint128</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">5745f1bf80414e0ad2670e85d6aece8c58031def</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/inconshreveable/mousetrap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">76626ae9c91c4f2a10f34cad8ce83ea42c93bb75</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/pkg/errors</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">248dadf4e9068a0b3e79f02ed0a610d935de5302</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/seehuhn/mt19937</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">98c0ea580d2f3c5a171acf4d4f15321b72209d08</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spf13/cobra</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">6b74a60562f5c1c920299b8f02d153e16f4897fc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spf13/pflag</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">5ccb023bc27df288a957c5e994cd44fd19619465</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spiegel-im-spiegel/gocli</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">5929f04fb8e4a19ac29fdf658866f9441f339cd9</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">testImports</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span></span></span></code></pre></div><p>この状態で <code>dep init</code> コマンドを実行する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep init
</span></span><span class="line"><span class="cl">Importing configuration from glide. These are only initial constraints, and are further refined during the solve process.
</span></span><span class="line"><span class="cl">Detected glide configuration files...
</span></span><span class="line"><span class="cl">Converting from glide.yaml and glide.lock...
</span></span><span class="line"><span class="cl"> Trying v0.3.0 (5929f04) as initial lock for imported dep github.com/spiegel-im-spiegel/gocli
</span></span><span class="line"><span class="cl"> Trying * (6b74a60) as initial lock for imported dep github.com/spf13/cobra
</span></span><span class="line"><span class="cl"> Trying * (248dadf) as initial lock for imported dep github.com/pkg/errors
</span></span><span class="line"><span class="cl"> Trying master (98c0ea5) as initial lock for imported dep github.com/seehuhn/mt19937
</span></span><span class="line"><span class="cl"> Trying * (189780b) as initial lock for imported dep github.com/davidminor/gorand
</span></span><span class="line"><span class="cl"> Trying master (5745f1b) as initial lock for imported dep github.com/davidminor/uint128
</span></span><span class="line"><span class="cl"> Trying v1.0 (76626ae) as initial lock for imported dep github.com/inconshreveable/mousetrap
</span></span><span class="line"><span class="cl"> Trying * (5ccb023) as initial lock for imported dep github.com/spf13/pflag
</span></span><span class="line"><span class="cl"> Locking in (248dadf) for direct dep github.com/pkg/errors
</span></span><span class="line"><span class="cl"> Locking in (6b74a60) for direct dep github.com/spf13/cobra
</span></span><span class="line"><span class="cl"> Using master as constraint for direct dep github.com/seehuhn/mt19937
</span></span><span class="line"><span class="cl"> Locking in master (98c0ea5) for direct dep github.com/seehuhn/mt19937
</span></span><span class="line"><span class="cl"> Using ^0.3.0 as constraint for direct dep github.com/spiegel-im-spiegel/gocli
</span></span><span class="line"><span class="cl"> Locking in v0.3.0 (5929f04) for direct dep github.com/spiegel-im-spiegel/gocli
</span></span><span class="line"><span class="cl"> Locking in (189780b) for direct dep github.com/davidminor/gorand
</span></span></code></pre></div><p>実は <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> パッケージの最新版は v0.5.0 だが, <code>glide.lock</code> の内容を読み取って,ちゃんと v0.3.0 のものを取ってきているようだ。
偉いぞ!</p>
<p><code>dep init</code> コマンドにより <a href="https://golang.github.io/dep/docs/Gopkg.toml.html" title="Gopkg.toml · dep"><code>Gopkg.toml</code></a> および <a href="https://golang.github.io/dep/docs/Gopkg.lock.html" title="Gopkg.lock · dep"><code>Gopkg.lock</code></a> の2つのファイルと <code>vendor/</code> フォルダが作成される。
このうち <a href="https://golang.github.io/dep/docs/Gopkg.toml.html" title="Gopkg.toml · dep"><code>Gopkg.toml</code></a> の内容は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/seehuhn/mt19937"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.3.0"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">prune</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">go-tests</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nx">unused-packages</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>そして <a href="https://golang.github.io/dep/docs/Gopkg.lock.html" title="Gopkg.lock · dep"><code>Gopkg.lock</code></a> の内容は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/davidminor/gorand"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"lcg"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"189780b8053a44a111339a4248394fd844c1da40"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/davidminor/uint128"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"5745f1bf80414e0ad2670e85d6aece8c58031def"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/inconshreveable/mousetrap"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"v1.0"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"248dadf4e9068a0b3e79f02ed0a610d935de5302"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/seehuhn/mt19937"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"98c0ea580d2f3c5a171acf4d4f15321b72209d08"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"6b74a60562f5c1c920299b8f02d153e16f4897fc"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/pflag"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"5ccb023bc27df288a957c5e994cd44fd19619465"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">projects</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">revision</span> <span class="p">=</span> <span class="s2">"5929f04fb8e4a19ac29fdf658866f9441f339cd9"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"v0.3.0"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">solve-meta</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">analyzer-name</span> <span class="p">=</span> <span class="s2">"dep"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">analyzer-version</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">inputs-digest</span> <span class="p">=</span> <span class="s2">"3f9a0c0024e81ba251efaa0cb0014694f8315add84c7e8044a346f370e3e088e"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">solver-name</span> <span class="p">=</span> <span class="s2">"gps-cdcl"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">solver-version</span> <span class="p">=</span> <span class="mi">1</span>
</span></span></code></pre></div><p><code>glide.lock</code> と <a href="https://golang.github.io/dep/docs/Gopkg.lock.html" title="Gopkg.lock · dep"><code>Gopkg.lock</code></a> の内容がマッチしているのが分かると思う。</p>
<div class="box"><strong>【2018-02-02 追記】</strong> dep v0.4 から挙動が変わった?
どうやら <code>dep init</code> 時点でリビジョン管理が必要と判断されるパッケージのみ <code>Gopkg.toml</code> に記載される感じ。
それ以外でリビジョン管理が必要なものは手動で <code>Gopkg.toml</code> に記述する必要があるかも。</div>
<p>念のため <code>dep status</code> も見ておこう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep status
</span></span><span class="line"><span class="cl">PROJECT CONSTRAINT VERSION REVISION LATEST PKGS USED
</span></span><span class="line"><span class="cl">github.com/davidminor/gorand * 189780b 1
</span></span><span class="line"><span class="cl">github.com/davidminor/uint128 branch master branch master 5745f1b 5745f1b 1
</span></span><span class="line"><span class="cl">github.com/inconshreveable/mousetrap v1.0 v1.0 76626ae v1.0 1
</span></span><span class="line"><span class="cl">github.com/pkg/errors * 248dadf 1
</span></span><span class="line"><span class="cl">github.com/seehuhn/mt19937 branch master branch master 98c0ea5 98c0ea5 1
</span></span><span class="line"><span class="cl">github.com/spf13/cobra * 6b74a60 1
</span></span><span class="line"><span class="cl">github.com/spf13/pflag * 5ccb023 1
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/gocli ^0.3.0 v0.3.0 5929f04 v0.3.0 1
</span></span></code></pre></div><p>ビルドもちゃんと通る。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go build -v .
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/uint128
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/pflag
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/seehuhn/mt19937
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/inconshreveable/mousetrap
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spiegel-im-spiegel/gocli
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/gorand/lcg
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/pkg/errors
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/gencmplx
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/qq
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/genpi
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/plot
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/estmt
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/cmd
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi
</span></span></code></pre></div><p>というわけで, <code>glide.yaml</code> と <code>glide.lock</code> が正しい状態で残っていれば問題なく <a href="https://golang.github.io/dep/" title="dep · Dependency management for Go">dep</a> に移行できそうだ。</p>
<h2>依存関係の管理</h2>
<p><a href="https://golang.github.io/dep/docs/Gopkg.toml.html" title="Gopkg.toml · dep"><code>Gopkg.toml</code></a> を以下のように修正してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/davidminor/gorand"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.8.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/seehuhn/mt19937"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.0.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.3.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">prune</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">go-tests</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nx">unused-packages</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>この状態で <code>dep ensure</code> コマンドを実行しステータスを見ると,以下のような感じになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep status
</span></span><span class="line"><span class="cl">PROJECT CONSTRAINT VERSION REVISION LATEST PKGS USED
</span></span><span class="line"><span class="cl">github.com/davidminor/gorand branch master branch master 283446f 283446f 1
</span></span><span class="line"><span class="cl">github.com/davidminor/uint128 branch master branch master 5745f1b 5745f1b 1
</span></span><span class="line"><span class="cl">github.com/inconshreveable/mousetrap v1.0 v1.0 76626ae v1.0 1
</span></span><span class="line"><span class="cl">github.com/pkg/errors ^0.8.0 v0.8.0 645ef00 v0.8.0 1
</span></span><span class="line"><span class="cl">github.com/seehuhn/mt19937 branch master branch master 98c0ea5 98c0ea5 1
</span></span><span class="line"><span class="cl">github.com/spf13/cobra ^0.0.1 v0.0.1 7b2c5ac v0.0.1 1
</span></span><span class="line"><span class="cl">github.com/spf13/pflag * 5ccb023 1
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/gocli ^0.3.0 v0.3.0 5929f04 v0.3.0 1
</span></span></code></pre></div><p>たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/davidminor/gorand"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span></code></pre></div><p>であれば <a href="https://github.com/davidminor/gorand" title="davidminor/gorand: Basic golang implementation of a permuted congruential generator for pseudorandom number generation">github.com/davidminor/gorand</a> パッケージで master ブランチの最新コミットを取ってくる。
また</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.3.*"</span>
</span></span></code></pre></div><p>であれば <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> パッケージで v0.3.x の最新バージョンを取ってくる<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p><code>[prune]</code> 指定では <code>vendor/</code> フォルダから除外するパッケージやファイルを指定する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">prune</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">go-tests</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nx">unused-packages</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p><code>go-tests</code> はテスト用のファイル(<code>*_test.go</code>)を <code>unused-packages</code> は未使用のパッケージを指す。
なお,値は <code>true</code> 以外はエラーになるようだ。
たとえば未使用パッケージも含めたいのであれば <code>unused-packages = false</code> とするのではなく記述自体を削除する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">prune</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">go-tests</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><h2>依存関係の視覚化</h2>
<p><code>dep status</code> コマンドには結果を DOT 言語で吐き出すオプションがあるようだ。
<a href="http://graphviz.org/" title="Graphviz - Graph Visualization Software">Graphviz</a> があれば,この出力結果を画像データに変換できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep status -dot | dot -Tpng -o pi-dependency.png
</span></span></code></pre></div><p>結果はこんな感じ。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./pi-dependency.png"><img src="./pi-dependency.png" srcset="./pi-dependency.png 1275w" sizes="(min-width:600px) 500px, 80vw" alt="pi-dependency.png" loading="lazy"></a><figcaption><div><a href="./pi-dependency.png">pi-dependency.png</a></div></figcaption>
</figure>
<p>ブラボー!</p>
<h2>リポジトリへのパスを直接指定する</h2>
<p>GitHub みたいな有名 SaaS に置いてあるパッケージなら <a href="https://golang.github.io/dep/docs/Gopkg.toml.html" title="Gopkg.toml · dep"><code>Gopkg.toml</code></a> に</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.3.*"</span>
</span></span></code></pre></div><p>とか書けば適切に処理してくれるけど,有名でない SaaS ディレクトリや職場 LAN のリポジトリ上のパッケージではこうはいかないこともある。
こういう場合には,以下に示す通り,直接リポジトリへの(プロトコルを含めた)パスを指定する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">source</span> <span class="p">=</span> <span class="s2">"git@github.com:spiegel-im-spiegel/gocli.git"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.3.* "</span></span></span></code></pre></div>
<p>これで <code>dep ensure</code> すれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep ensure -v
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">(1/8) Wrote github.com/spiegel-im-spiegel/gocli (from git@github.com:spiegel-im-spiegel/gocli.git)@v0.3.0
</span></span><span class="line"><span class="cl">(2/8) Wrote github.com/pkg/errors@248dadf4e9068a0b3e79f02ed0a610d935de5302
</span></span><span class="line"><span class="cl">(3/8) Wrote github.com/davidminor/gorand@189780b8053a44a111339a4248394fd844c1da40
</span></span><span class="line"><span class="cl">(4/8) Wrote github.com/spf13/pflag@5ccb023bc27df288a957c5e994cd44fd19619465
</span></span><span class="line"><span class="cl">(5/8) Wrote github.com/inconshreveable/mousetrap@v1.0
</span></span><span class="line"><span class="cl">(6/8) Wrote github.com/davidminor/uint128@master
</span></span><span class="line"><span class="cl">(7/8) Wrote github.com/spf13/cobra@6b74a60562f5c1c920299b8f02d153e16f4897fc
</span></span><span class="line"><span class="cl">(8/8) Wrote github.com/seehuhn/mt19937@master</span></span></code></pre></div>
<p>といった感じになる。
ちゃんと指定したリポジトリからパッケージを取得してきているのが分かるだろう。</p>
<h2>Go 1.9 から glide novendor は必要なくなった</h2>
<p>Vendoring で一番あつかいに困るのがテストで,たとえば安直に</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v ./...
</span></span></code></pre></div><p>とかやると <code>vendor/</code> フォルダ以下のパッケージまでテスト・シーケンスが走ってしまうのが困りものであった。
このため <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> にはこれを回避する <code>glide novendor</code> コマンドがあって</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v $(glide novendor)
</span></span></code></pre></div><p>とすることで <code>vendor/</code> フォルダへのテストを回避できるようになっていたのだ<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<p>ところがところが!!</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a> 1.9 からは <code>./...</code> の扱いが変更になり</p>
<figure lang="en">
<blockquote>“By popular request, <code>./...</code> no longer matches packages in <code>vendor</code> directories in tools accepting package names, such as <code>go test</code>.”</blockquote>
<figcaption><div>via <q><a href="https://golang.org/doc/go1.9#vendor-dotdotdot">Go 1.9 Release Notes</a></q></div></figcaption>
</figure>
<p>ということで <code>./...</code> に <code>vendor/</code> フォルダ以下が含まれないことになったのだ。
たとえば <a href="https://github.com/spiegel-im-spiegel/pi" title="spiegel-im-spiegel/pi: Estimate of Pi with Monte Carlo method.">spiegel-im-spiegel/pi</a> パッケージの場合は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go list ./...
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/cmd
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/estmt
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/gencmplx
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/genpi
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/plot
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/qq
</span></span></code></pre></div><p>となる。
逆に <code>vendor/</code> フォルダも含めたいなら</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go list ./... ./vendor/...
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/cmd
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/estmt
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/gencmplx
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/genpi
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/plot
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/qq
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/gorand/lcg
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/gorand/pcg
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/uint128
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/inconshreveable/mousetrap
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/pkg/errors
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/seehuhn/mt19937
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra/cobra
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra/cobra/cmd
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra/doc
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/pflag
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi/vendor/github.com/spiegel-im-spiegel/gocli
</span></span></code></pre></div><p>とすればよい。
こっちのほうが遥かに扱いやすいよね。</p>
<p>これでまたひとつ <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> が「要らない子」になる理由が増えてしまったのだった。</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="https://dev.classmethod.jp/go/dep/">Goオフィシャルチーム作成の依存関係管理ツール dep を試してみた | Developers.IO</a></p>
</li>
<li>
<p><a href="https://mattn.kaoriya.net/software/lang/go/20170125023240.htm">Big Sky :: golang オフィシャル謹製のパッケージ依存解決ツール「dep」</a></p>
</li>
<li>
<p><a href="https://qiita.com/noppefoxwolf/items/49bd460034a5c84e1956">[go]depでブランチ指定 - Qiita</a></p>
<ul>
<li><a href="https://golang.github.io/dep/docs/ensure-mechanics.html">Models and Mechanisms · dep</a></li>
</ul>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/gopath-pollution/">GOPATH 汚染問題</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/package-visualization-tool/">パッケージの依存状況の視覚化</a></p>
</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Windows 7 の場合は “<a href="https://www.microsoft.com/ja-jp/download/details.aspx?id=40855">Windows Management Framework 4.0</a>” をインストールすることで PowerShell 4.0 にアップグレードできる。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><a href="https://golang.github.io/dep/docs/Gopkg.toml.html" title="Gopkg.toml · dep"><code>Gopkg.toml</code></a> のバージョンの考え方は “<a href="http://semver.org/" title="Semantic Versioning 2.0.0 | Semantic Versioning">Semantic Versioning</a>” に従っている。ワイルドカード等を使ったバージョン指定については <a href="https://github.com/Masterminds/semver" title="Masterminds/semver: Work with Semantic Versions in Go">Masterminds/semver</a> パッケージを参照するとよい。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p><a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> を使わない場合は <code>go test -v $(go list ./... | grep -v /vendor/)</code> とかする。どのみち Windows のコマンドプロンプトでは無理だけど(笑) <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Glide で Vendoring
tag:text.Baldanders.info,2015-12-07:/golang/vendoring-with-glide/
2015-12-07T14:30:21+00:00
2019-07-01T13:48:09+00:00
Go 言語 1.5 の vendoring 機能をサポートするツールが glide である。
Spiegel
https://baldanders.info/profile/
<p><strong>【2017-10-10 追記】</strong>
<a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> プロジェクトは Vendoring ツールとして公式ツールの <a href="https://github.com/golang/dep" title="golang/dep: Go dependency management tool">dep</a> を推し始めたようである。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/consider-switching-from-glide-to-dep/">Glide から Dep への移行を検討する</a></li>
</ul>
<p>そこで当セクションでも今後は <a href="https://github.com/golang/dep" title="golang/dep: Go dependency management tool">dep</a> を推していくことにする。
この記事はメンテナンスされない可能性があるが,あしからずご了承の程を。</p>
<hr>
<p>以前「<a href="https://text.baldanders.info/golang/gopath-pollution/">GOPATH 汚染問題</a>」で <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a> 1.5 の vendoring 機能を紹介したが,この vendoring のヘルパ・ツールと言えるのが <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> である。
<a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> では依存する外部パッケージの情報を YAML 形式の定義ファイルに記述し,この定義ファイルを基に外部パッケージの管理を行う。</p>
<p>(<a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> 0.8 から挙動が少し変わった。
この記事では 0.8.2 を使っている)</p>
<h2>Glide のインストール</h2>
<p><a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> は自身も <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> で外部パッケージを管理している。
なので最初は <code>go get</code> ではなく <a href="https://github.com/Masterminds/glide/releases">Releases</a> ページからビルド済みのものを取得することをお勧めする<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<p>既に <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> が利用可能な状態なら,以下の要領でビルドできる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\glide>SET GOPATH=C:\workspace\glide
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide>SET GO15VENDOREXPERIMENT=1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide>git clone git@github.com:Masterminds/glide.git src\github.com\Masterminds\glide
</span></span><span class="line"><span class="cl">Cloning into 'src\github.com\Masterminds\glide'...
</span></span><span class="line"><span class="cl">remote: Counting objects: 2292, done.
</span></span><span class="line"><span class="cl">remote: Compressing objects: 100% (65/65), done.
</span></span><span class="line"><span class="cl">remote: Total 2292 (delta 34), reused 0 (delta 0), pack-reused 2292
</span></span><span class="line"><span class="cl">Receiving objects: 100% (2292/2292), 468.01 KiB | 397.00
</span></span><span class="line"><span class="cl">Receiving objects: 100% (2292/2292), 537.97 KiB | 397.00 KiB/s, done.
</span></span><span class="line"><span class="cl">Resolving deltas: 100% (1578/1578), done.
</span></span><span class="line"><span class="cl">Checking connectivity... done.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide>pushd src\github.com\Masterminds\glide
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide\src\github.com\Masterminds\glide>glide install
</span></span><span class="line"><span class="cl">[INFO] Fetching updates for github.com/codegangsta/cli.
</span></span><span class="line"><span class="cl">[INFO] Fetching updates for github.com/Masterminds/cookoo.
</span></span><span class="line"><span class="cl">[INFO] Fetching updates for github.com/Masterminds/vcs.
</span></span><span class="line"><span class="cl">[INFO] Fetching updates for gopkg.in/yaml.v2.
</span></span><span class="line"><span class="cl">[INFO] Fetching updates for github.com/Masterminds/semver.
</span></span><span class="line"><span class="cl">[INFO] Setting version for github.com/Masterminds/cookoo to 78aa11ce75e257c51be7ea945edb84cf19c4a6de.
</span></span><span class="line"><span class="cl">[INFO] Setting version for github.com/Masterminds/semver to 6333b7bd29aad1d79898ff568fd90a8aa533ae82.
</span></span><span class="line"><span class="cl">[INFO] Setting version for github.com/codegangsta/cli to b5232bb2934f606f9f27a1305f1eea224e8e8b88.
</span></span><span class="line"><span class="cl">[INFO] Setting version for github.com/Masterminds/vcs to eaee272c8fa4514e1572e182faecff5be20e792a.
</span></span><span class="line"><span class="cl">[INFO] Setting version for gopkg.in/yaml.v2 to f7716cbe52baa25d2e9b0d0da546fcf909fc16b4.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide\src\github.com\Masterminds\glide>popd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide>go install -v ./...
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/io
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/msg
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/gb
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/gopkg.in/yaml.v2
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/semver
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/codegangsta/cli
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/safely
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/cli
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/database/sql
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/database/active
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/convert
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/fmt
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/web
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/example
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/log
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/web/auth
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/util
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/doc
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/cfg
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/dependency
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide/cmd
</span></span><span class="line"><span class="cl">github.com/Masterminds/glide
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\glide>bin\glide.exe -v
</span></span><span class="line"><span class="cl">glide version dev
</span></span></code></pre></div><p>簡単な使い方は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:> glide -h
</span></span><span class="line"><span class="cl">NAME:
</span></span><span class="line"><span class="cl"> glide - The lightweight vendor package manager for your Go projects.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Each project should have a 'glide.yaml' file in the project directory. Files
</span></span><span class="line"><span class="cl">look something like this:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> package: github.com/Masterminds/glide
</span></span><span class="line"><span class="cl"> imports:
</span></span><span class="line"><span class="cl"> - package: github.com/Masterminds/cookoo
</span></span><span class="line"><span class="cl"> vcs: git
</span></span><span class="line"><span class="cl"> ref: 1.1.0
</span></span><span class="line"><span class="cl"> subpackages: **
</span></span><span class="line"><span class="cl"> - package: github.com/kylelemons/go-gypsy
</span></span><span class="line"><span class="cl"> subpackages: yaml
</span></span><span class="line"><span class="cl"> flatten: true
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">NOTE: As of Glide 0.5, the commands 'in', 'into', 'gopath', 'status', and 'env'
</span></span><span class="line"><span class="cl">no longer exist.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">USAGE:
</span></span><span class="line"><span class="cl"> glide [global options] command [command options] [arguments...]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">VERSION:
</span></span><span class="line"><span class="cl"> 0.8.2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">COMMANDS:
</span></span><span class="line"><span class="cl"> create, init Initialize a new project, creating a glide.yaml file
</span></span><span class="line"><span class="cl"> get Install one or more packages into `vendor/` and add dependency to glide.yaml.
</span></span><span class="line"><span class="cl"> import Import files from other dependency management systems.
</span></span><span class="line"><span class="cl"> name Print the name of this project.
</span></span><span class="line"><span class="cl"> novendor, nv List all non-vendor paths in a directory.
</span></span><span class="line"><span class="cl"> rebuild Rebuild ('go build') the dependencies
</span></span><span class="line"><span class="cl"> install, i Install a project's dependencies
</span></span><span class="line"><span class="cl"> update, up Update a project's dependencies
</span></span><span class="line"><span class="cl"> tree Tree prints the dependencies of this project as a tree.
</span></span><span class="line"><span class="cl"> list List prints all dependencies that Glide could discover.
</span></span><span class="line"><span class="cl"> about Learn about Glide
</span></span><span class="line"><span class="cl"> help, h Shows a list of commands or help for one command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">GLOBAL OPTIONS:
</span></span><span class="line"><span class="cl"> --yaml, -y "glide.yaml" Set a YAML configuration file.
</span></span><span class="line"><span class="cl"> --quiet, -q Quiet (no info or debug messages)
</span></span><span class="line"><span class="cl"> --debug Print Debug messages (verbose)
</span></span><span class="line"><span class="cl"> --home "C:\Users\username\.glide" The location of Glide files [$GLIDE_HOME]
</span></span><span class="line"><span class="cl"> --no-color Turn off colored output for log messages
</span></span><span class="line"><span class="cl"> --help, -h show help
</span></span><span class="line"><span class="cl"> --version, -v print the version
</span></span></code></pre></div><h2>開発環境の準備</h2>
<p>動作検証用に「<a href="https://text.baldanders.info/golang/gopath-pollution/">GOPATH 汚染問題</a>」で使ったコードを利用する。
まず,以下の環境を作る。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\vdemo2>SET GOPATH=C:\workspace\vdemo2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2>SET GO15VENDOREXPERIMENT=1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\VDEMO2
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> julian-day.go
</span></span></code></pre></div><p><code>julian-day.go</code> の内容は以下のとおりである。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"flag"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strconv"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/astrocalc/modjulian"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//引数のチェック
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">flag</span><span class="p">.</span><span class="nf">Parse</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">argsStr</span> <span class="o">:=</span> <span class="nx">flag</span><span class="p">.</span><span class="nf">Args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">argsStr</span><span class="p">)</span> <span class="p"><</span> <span class="mi">3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"年月日を指定してください"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">args</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">num</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">argsStr</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">args</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">num</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tm</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Date</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Month</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span> <span class="nx">args</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">UTC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%v\n"</span><span class="p">,</span> <span class="nx">tm</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"MJD = %d日\n"</span><span class="p">,</span> <span class="nx">modjulian</span><span class="p">.</span><span class="nf">DayNumber</span><span class="p">(</span><span class="nx">tm</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>当然ながら,このままビルドしても外部パッケージがないため失敗する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\vdemo2>go install ./...
</span></span><span class="line"><span class="cl">src\julian-day\julian-day.go:10:2: cannot find package "github.com/spiegel-im-spiegel/astrocalc/modjulian" in any of:
</span></span><span class="line"><span class="cl"> C:\Go\src\github.com\spiegel-im-spiegel\astrocalc\modjulian (from $GOROOT)
</span></span><span class="line"><span class="cl"> C:\workspace\vdemo2\src\github.com\spiegel-im-spiegel\astrocalc\modjulian (from $GOPATH)
</span></span></code></pre></div><h2>依存関係を定義する</h2>
<p>開発環境ができたら,パッケージのフォルダ(今回は <code>src/julian-day</code>)に移動し, <code>glide create</code> コマンドで依存関係を定義する <code>glide.yaml</code> ファイルを生成する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\vdemo2>pushd src\julian-day
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2\src\julian-day>glide create
</span></span><span class="line"><span class="cl">[INFO] Generating a YAML configuration file and guessing the dependencies
</span></span><span class="line"><span class="cl">[INFO] Attempting to import from other package managers (use --skip-import to skip)
</span></span><span class="line"><span class="cl">[INFO] Found reference to github.com\spiegel-im-spiegel\astrocalc\modjulian
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2\src\julian-day>tree /f C:\workspace\vdemo2
</span></span><span class="line"><span class="cl">C:\WORKSPACE\VDEMO2
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> glide.yaml
</span></span><span class="line"><span class="cl"> julian-day.go
</span></span></code></pre></div><p>ソースコードを読んで <code>glide.yaml</code> に必要な情報を推測して書いてくれているようだが<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>,デリミタが “/” じゃなくて “\” になってる。
Windows 環境だからかな。
残念 orz</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">julian-day</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">import</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com\spiegel-im-spiegel\astrocalc\modjulian</span><span class="w">
</span></span></span></code></pre></div><p><a href="https://github.com/spiegel-im-spiegel/astrocalc" title="spiegel-im-spiegel/astrocalc">astrocalc</a>/modjulian パッケージのパスを修正して、更にバージョン情報を追加する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">julian-day</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">import</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spiegel-im-spiegel/astrocalc/modjulian</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v0.1.0</span><span class="w">
</span></span></span></code></pre></div><p>これで <code>go get</code> コマンドと同じように, <code>package</code> のパスから自動的に repository を判別してパッケージを取得できる。
<code>go get</code> コマンドと異なるのは, <code>glide.yaml</code> ファイルで指定した <code>version</code> 情報から適切な revision を選択できる点である<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<p>また,以下のように VCS (Version Control System) の種類<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> と URI を明示的に指定することもできる(<code>vcs</code> と <code>repo</code> は必ずセットで指定する)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">julian-day</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">import</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spiegel-im-spiegel/astrocalc/modjulian</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">vcs</span><span class="p">:</span><span class="w"> </span><span class="l">git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">repo</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:spiegel-im-spiegel/astrocalc.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v0.1.0</span><span class="w">
</span></span></span></code></pre></div><p>たとえば,プライベートな bare repository からインポートする場合には,この方法が有効である。</p>
<h2>パッケージの取得とビルド</h2>
<p>パッケージの取得には <code>glide update</code> または <code>glide up</code> コマンドを起動する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\vdemo2\src\julian-day>glide up
</span></span><span class="line"><span class="cl">[INFO] Fetching updates for github.com/spiegel-im-spiegel/astrocalc.
</span></span><span class="line"><span class="cl">[INFO] Setting version for github.com/spiegel-im-spiegel/astrocalc to v0.1.0.
</span></span><span class="line"><span class="cl">[INFO] Scanning github.com/spiegel-im-spiegel/astrocalc for dependencies.
</span></span><span class="line"><span class="cl">[INFO] Setting version for github.com/spiegel-im-spiegel/astrocalc to v0.1.0.
</span></span><span class="line"><span class="cl">[INFO] Project relies on 1 dependencies.
</span></span><span class="line"><span class="cl">[INFO] Writing glide.lock file
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2\src\julian-day>tree /f C:\workspace\vdemo2
</span></span><span class="line"><span class="cl">C:\WORKSPACE\VDEMO2
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> │ glide.lock
</span></span><span class="line"><span class="cl"> │ glide.yaml
</span></span><span class="line"><span class="cl"> │ julian-day.go
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └─vendor
</span></span><span class="line"><span class="cl"> └─github.com
</span></span><span class="line"><span class="cl"> └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl"> └─astrocalc
</span></span><span class="line"><span class="cl"> │ .editorconfig
</span></span><span class="line"><span class="cl"> │ .gitignore
</span></span><span class="line"><span class="cl"> │ .travis.yml
</span></span><span class="line"><span class="cl"> │ LICENSE
</span></span><span class="line"><span class="cl"> │ README.md
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └─modjulian
</span></span><span class="line"><span class="cl"> example_test.go
</span></span><span class="line"><span class="cl"> LICENSE
</span></span><span class="line"><span class="cl"> modjulian.go
</span></span><span class="line"><span class="cl"> modjulian_test.go
</span></span></code></pre></div><p>これでビルドが可能になった。
ではビルドしようかな。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\vdemo2\src\julian-day>popd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2>go install -v ./...
</span></span><span class="line"><span class="cl">julian-day/vendor/github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl">julian-day
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\vdemo2>bin\julian-day.exe 2015 1 1
</span></span><span class="line"><span class="cl">2015-01-01 00:00:00 +0000 UTC
</span></span><span class="line"><span class="cl">MJD = 57023日
</span></span></code></pre></div><p>よーし,うむうむ,よーし。</p>
<h2>Vendor フォルダの管理</h2>
<p><code>glide.lock</code> ファイルには外部パッケージの状態が記述されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">hash</span><span class="p">:</span><span class="w"> </span><span class="l">b1eecd43769aa1225f7b8922a35566008561c64f4ce11180f299888ed153ad69</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">updated</span><span class="p">:</span><span class="w"> </span><span class="ld">2015-12-23T10:47:22.4140434</span><span class="m">+09</span><span class="p">:</span><span class="m">00</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">imports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/spiegel-im-spiegel/astrocalc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">256cd3bd4dc63441d3f4759d761593a147d51d88</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">subpackages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">modjulian</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">devImports</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span></span></span></code></pre></div><p><code>glide.lock</code> があれば <code>glide install</code>(または <code>glide i</code>)コマンドで <code>vendor</code> フォルダ以下の状態を復元できる。</p>
<p><a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> では外部パッケージを vendor フォルダ以下に repository 構造ごと展開する。
この場合,開発対象のパッケージも repository で管理しているのだから, repository が入れ子になり具合が悪い。
その辺,当の <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> はどうしてるのかなぁと思ったら <code>.gitignore</code> ファイルで <code>vendor/</code> を除外対象にしていた。
なるほど,そりゃそうか。</p>
<p><code>glide.yaml</code> および <code>glide.lock</code> ファイルの管理さえちゃんとしていれば <code>glide install</code> コマンドでいつでも復元できるのだから <code>vendor</code> フォルダ以下を除外しても問題ないわけだ<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。
これは複数メンバで開発環境を共有する際には便利な機能である。</p>
<p>また vendoring に対応していない(Go 1.4 以下の)環境や <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> がない環境では <code>go get</code> で外部パッケージを取ってくることで(revision 等の問題はあるけど)一応ビルドは通る。</p>
<p>更に言うと, <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> は <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の標準機能に準拠しているため,他のサポートツールとの相性がいいのも利点だろう。
たとえば, <a href="https://text.baldanders.info/golang/golang-with-atom/">ATOM ベースの開発環境</a>は <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> と相性がいい<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>。
あと,(多少強引な手を使っているが<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>) <a href="https://travis-ci.org/">Travis CI</a> のような CI (Continuous Integration) と組み合わせることも難しくない。</p>
<p>こう考えると <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> は<a href="https://text.baldanders.info/golang/project-based-development/">前に紹介</a>した <a href="http://getgb.io/">gb</a> よりも筋がいいツールといえるかもしれない。</p>
<h2>おまけ: vender フォルダ以下をテストから除外する</h2>
<p>テストを行う際に普通に</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v ./...
</span></span></code></pre></div><p>とかやると <code>vendor</code> フォルダ以下も対象になってしまう。
<code>vendor</code> フォルダ以下を除外したいのであれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v $(glide novendor)
</span></span></code></pre></div><p>とすればよい。</p>
<p>ただこれ Windows のコマンドプロンプトでは使えないんだよねぇ。
パイプで <code>go test</code> に渡してもうまくいかない感じ。
そういう場合は <code>glide novendor</code> の実行結果を整形してバッチファイルにするしかないのだろう。
やれやれ。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://qiita.com/tienlen/items/8e192e68d6b18bec3b4a">glide - パッケージ管理のお困りの方へ - - Qiita</a></li>
<li><a href="http://qiita.com/ktsujichan/items/c78e2515c459316cb1f6">Golangでプロジェクト内のテストを全件実行する - Qiita</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Mac 環境なら brew でインストールできるらしい。 Linux 等の環境であれば <code>make</code> コマンドで各種プラットフォームの実行ファイルをビルドできる。一応 <code>go get</code> でもビルドできるが, revision を制御できないので失敗する可能性もある(これは <code>make</code> コマンドでビルドする場合でも同じだけど)。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><code>glide create</code> 時の依存パッケージの推測を抑止するには <code>--skip-import</code> オプションを付ける。のだが,効いてないな。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>今回であれば repository の <a href="https://github.com/spiegel-im-spiegel/astrocalc/releases/tag/v0.1.0"><code>v0.1.0</code></a> タグに対応する revision を選択する。バージョンの記述形式は <code>package.json</code> と同じように記述でき,バージョンの解釈は <a href="http://semver.org/">Semantic Versioning</a> に従っている。ちなみに revision ID を直接指定することもできる。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p><a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> では <a href="http://git-scm.com/">git</a> のほか svn (<a href="http://subversion.apache.org/">Subversion</a>), hg (<a href="http://mercurial.selenic.com/">Mercurial</a>), bzr (<a href="http://bazaar.canonical.com/">Bazaar</a>) が利用可能である。 <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p><code>vendor</code> フォルダ以下は外部パッケージなので通常はさわることはない。 <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:6">
<p>残念ながら,「<a href="https://text.baldanders.info/golang/package-visualization-tool/">パッケージの依存状況の視覚化</a>」ツールは vendoring 機能に対応していないため上手く表示できない。なお, <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> では <code>glide list</code> および <code>glide tree</code> で依存パッケージを見ることができる。 <a href="#fnref:6" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:7">
<p><a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> の <a href="https://github.com/Masterminds/glide/blob/master/.travis.yml"><code>.travis.yml</code></a> や <a href="https://github.com/Masterminds/glide/blob/master/Makefile"><code>Makefile</code></a> を参照。 <a href="#fnref:7" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
gb でプロジェクト・ベースの開発環境をつくる
tag:text.Baldanders.info,2015-09-28:/golang/project-based-development/
2015-09-28T11:38:45+00:00
2019-07-01T13:48:09+00:00
今回は gb を使ってプロジェクト・ベースで Golang のコードを管理してみる。
Spiegel
https://baldanders.info/profile/
<p>(初出: <a href="http://qiita.com/spiegel-im-spiegel/items/ef15a48542e043b32c99">はじめての Go 言語 (on Windows) その9 - Qiita</a>)</p>
<p>今回は <a href="http://getgb.io/" title="gb - A project based build tool for Go">gb</a> を使ってプロジェクト・ベースで Golang のコードを管理してみる。</p>
<ul>
<li><a href="http://getgb.io/">gb - A project based build tool for Go</a></li>
</ul>
<h2>gb の導入</h2>
<p><a href="http://getgb.io/" title="gb - A project based build tool for Go">gb</a> の導入は <code>go get</code> でできる<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:>go get -v github.com/constabulary/gb/...
</span></span><span class="line"><span class="cl">github.com/constabulary/gb (download)
</span></span><span class="line"><span class="cl">github.com/constabulary/gb/log
</span></span><span class="line"><span class="cl">github.com/constabulary/gb
</span></span><span class="line"><span class="cl">github.com/constabulary/gb/vendor
</span></span><span class="line"><span class="cl">github.com/constabulary/gb/cmd
</span></span><span class="line"><span class="cl">github.com/constabulary/gb/cmd/gb
</span></span><span class="line"><span class="cl">github.com/constabulary/gb/cmd/gb-vendor
</span></span></code></pre></div><p>Windows の場合,環境変数 <code>GOPATH</code> で指定するフォルダ配下の <code>bin</code> フォルダに <code>gb.exe</code> および <code>gb-vendor.exe</code> が生成される。
このフォルダにパスを通しておく(またはパスの通っているフォルダに実行ファイルをコピーする)。</p>
<h2>プロジェクトの構築とビルド</h2>
<p>「<a href="https://text.baldanders.info/golang/packaging/">機能のパッケージ化</a>」で最後に作ったコードを使って実際に <a href="http://getgb.io/" title="gb - A project based build tool for Go">gb</a> でプロジェクトを作成しビルドを行ってみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"flag"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strconv"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/astrocalc/modjulian"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//引数のチェック
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">flag</span><span class="p">.</span><span class="nf">Parse</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">argsStr</span> <span class="o">:=</span> <span class="nx">flag</span><span class="p">.</span><span class="nf">Args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">argsStr</span><span class="p">)</span> <span class="p"><</span> <span class="mi">3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"年月日を指定してください"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">args</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">num</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">argsStr</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">args</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">num</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tm</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Date</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Month</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span> <span class="nx">args</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">UTC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%v\n"</span><span class="p">,</span> <span class="nx">tm</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"MJD = %d日\n"</span><span class="p">,</span> <span class="nx">modjulian</span><span class="p">.</span><span class="nf">DayNumber</span><span class="p">(</span><span class="nx">tm</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3>ソース・ファイルの配置</h3>
<p>プロジェクト・フォルダを <code>C:\workspace\gbdemo</code> とし,ソース・ファイル用のフォルダ <code>src\julian-day</code> を作成する。
このフォルダに上述のコードを記述したソース・ファイルを配置する。
フォルダ構成は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\GBDEMO
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> julian-day.go
</span></span></code></pre></div><p>ビルドするには <code>gb build</code> コマンドを実行すればいいのだが,このままでは <code>modjulian</code> パッケージがないため失敗する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb build
</span></span><span class="line"><span class="cl">FATAL command "build" failed: failed to resolve import path "julian-day": cannot find package "github.com/spiegel-im-spiegel/astrocalc/modjulian" in any of:
</span></span><span class="line"><span class="cl"> C:\Go\src\github.com\spiegel-im-spiegel\astrocalc\modjulian (from $GOROOT)
</span></span><span class="line"><span class="cl"> C:\workspace\gbdemo\src\github.com\spiegel-im-spiegel\astrocalc\modjulian (from $GOPATH)
</span></span><span class="line"><span class="cl"> C:\workspace\gbdemo\vendor\src\github.com\spiegel-im-spiegel\astrocalc\modjulian
</span></span></code></pre></div><p>プロジェクト・フォルダ以下を <code>GOPATH</code> として <code>modjulian</code> パッケージを探しているのがお分かりだろうか。
<a href="http://getgb.io/" title="gb - A project based build tool for Go">gb</a> では,実行時に既存の <code>GOPATH</code> を上書きするようである。
またプロジェクト・フォルダ配下の <code>vendor</code> フォルダを探しているのにも注目してほしい。</p>
<h3>外部パッケージの導入</h3>
<p><a href="http://getgb.io/" title="gb - A project based build tool for Go">gb</a> では外部パッケージを <code>gb vendor</code> コマンドで管理できる。
外部パッケージの導入には <code>gb vendor fetch</code> コマンドを使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb vendor fetch github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\GBDEMO
</span></span><span class="line"><span class="cl">├─src
</span></span><span class="line"><span class="cl">│ └─julian-day
</span></span><span class="line"><span class="cl">│ julian-day.go
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">└─vendor
</span></span><span class="line"><span class="cl"> │ manifest
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └─src
</span></span><span class="line"><span class="cl"> └─github.com
</span></span><span class="line"><span class="cl"> └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl"> └─astrocalc
</span></span><span class="line"><span class="cl"> └─modjulian
</span></span><span class="line"><span class="cl"> example_test.go
</span></span><span class="line"><span class="cl"> LICENSE
</span></span><span class="line"><span class="cl"> modjulian.go
</span></span><span class="line"><span class="cl"> modjulian_test.go
</span></span></code></pre></div><p>プロジェクト・フォルダ以下に <code>vendor</code> フォルダが作成され,パッケージのソースファイルが展開されている。</p>
<p>今回 <code>gb vendor fetch</code> で取得したパッケージは <a href="https://github.com/" title="GitHub">GitHub</a> のリポジトリから取ってきたものだが, <code>git clone</code> ではなく,フォルダ・ファイル構成ごとコピーしてきたもののようである。</p>
<p><code>gb vendor fetch</code> コマンドでは <code>-branch</code> や <code>-tag</code> や <code>-revision</code> オプションでリポジトリのブランチやタグまたはリビジョンを指定できる。
このとき,導入したパッケージのリポジトリ情報は <code>vender\manifest</code> ファイルに格納されている(中身は JSON 形式)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"version"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"dependencies"</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"importpath"</span><span class="p">:</span> <span class="s2">"github.com/spiegel-im-spiegel/astrocalc/modjulian"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"repository"</span><span class="p">:</span> <span class="s2">"https://github.com/spiegel-im-spiegel/astrocalc"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"revision"</span><span class="p">:</span> <span class="s2">"c9f5fb495e67b868a2b3f0e16c38282095fe5033"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"branch"</span><span class="p">:</span> <span class="s2">"master"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"path"</span><span class="p">:</span> <span class="s2">"/modjulian"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ちなみに外部パッケージをアップデートする場合は <code>gb vendor update</code> コマンドを使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb vendor update github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span></code></pre></div><p>または</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb vendor update -all
</span></span></code></pre></div><h3>プロジェクトのビルド</h3>
<p>では,この状態でもう一回ビルドしてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb build
</span></span><span class="line"><span class="cl">julian-day
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\GBDEMO
</span></span><span class="line"><span class="cl">├─bin
</span></span><span class="line"><span class="cl">│ julian-day.exe
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">├─pkg
</span></span><span class="line"><span class="cl">│ └─windows-amd64
</span></span><span class="line"><span class="cl">│ │ julian-day.a
</span></span><span class="line"><span class="cl">│ │
</span></span><span class="line"><span class="cl">│ └─github.com
</span></span><span class="line"><span class="cl">│ └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl">│ └─astrocalc
</span></span><span class="line"><span class="cl">│ modjulian.a
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">├─src
</span></span><span class="line"><span class="cl">│ └─julian-day
</span></span><span class="line"><span class="cl">│ julian-day.go
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">└─vendor
</span></span><span class="line"><span class="cl"> │ manifest
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └─src
</span></span><span class="line"><span class="cl"> └─github.com
</span></span><span class="line"><span class="cl"> └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl"> └─astrocalc
</span></span><span class="line"><span class="cl"> └─modjulian
</span></span><span class="line"><span class="cl"> example_test.go
</span></span><span class="line"><span class="cl"> LICENSE
</span></span><span class="line"><span class="cl"> modjulian.go
</span></span><span class="line"><span class="cl"> modjulian_test.go
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>bin\julian-day.exe 2015 1 1
</span></span><span class="line"><span class="cl">2015-01-01 00:00:00 +0000 UTC
</span></span><span class="line"><span class="cl">MJD = 57023日
</span></span></code></pre></div><p>今度は上手くいったようだ。
<code>gb build</code> コマンドのオプションは以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb help build
</span></span><span class="line"><span class="cl">usage: gb build [build flags] [packages]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Build compiles the packages named by the import paths, along with their
</span></span><span class="line"><span class="cl">dependencies.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> -f
</span></span><span class="line"><span class="cl"> ignore cached packages if present, new packages built will overwrite
</span></span><span class="line"><span class="cl"> any cached packages. This effectively disables incremental
</span></span><span class="line"><span class="cl"> compilation.
</span></span><span class="line"><span class="cl"> -F
</span></span><span class="line"><span class="cl"> do not cache packages, cached packages will still be used for
</span></span><span class="line"><span class="cl"> incremental compilation. -f -F is advised to disable the package
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> caching system.
</span></span><span class="line"><span class="cl"> -q
</span></span><span class="line"><span class="cl"> decreases verbosity, effectively raising the output level to ERROR.
</span></span><span class="line"><span class="cl"> In a successful build, no output will be displayed.
</span></span><span class="line"><span class="cl"> -P
</span></span><span class="line"><span class="cl"> The number of build jobs to run in parallel, including test execution.
</span></span><span class="line"><span class="cl"> By default this is the number of CPUs visible to gb.
</span></span><span class="line"><span class="cl"> -R
</span></span><span class="line"><span class="cl"> sets the base of the project root search path from the current working
</span></span><span class="line"><span class="cl"> directory to the value supplied. Effectively gb changes working
</span></span><span class="line"><span class="cl"> directory to this path before searching for the project root.
</span></span><span class="line"><span class="cl"> -v
</span></span><span class="line"><span class="cl"> increases verbosity, effectively lowering the output level from INFO
</span></span><span class="line"><span class="cl"> to DEBUG.
</span></span><span class="line"><span class="cl"> -dotfile
</span></span><span class="line"><span class="cl"> if provided, gb will output a dot formatted file of the build steps to
</span></span><span class="line"><span class="cl"> be performed.
</span></span><span class="line"><span class="cl"> -ldflags 'flag list'
</span></span><span class="line"><span class="cl"> arguments to pass on each linker invocation.
</span></span><span class="line"><span class="cl"> -gcflags 'arg list'
</span></span><span class="line"><span class="cl"> arguments to pass on each compile invocation.
</span></span><span class="line"><span class="cl"> -tags 'tag list'
</span></span><span class="line"><span class="cl"> additional build tags.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The list flags accept a space-separated list of strings. To embed spaces in an
</span></span><span class="line"><span class="cl">element in the list, surround it with either single or double quotes.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">For more about specifying packages, see 'gb help packages'. For more about
</span></span><span class="line"><span class="cl">where packages and binaries are installed, run 'gb help project'.
</span></span></code></pre></div><p><code>-ldflags</code> や <code>-gcflags</code> オプションが使えるのはありがたいかな。</p>
<h2>複数パッケージを含めたプロジェクト管理</h2>
<p>複数のパッケージをまとめて管理したい場合もある。
例えば以下のような構成を考えてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\GBDEMO
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> julian-day.go
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>pushd src
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo\src>git clone https://github.com/spiegel-im-spiegel/astrocalc.git github.com/spiegel-im-spiegel/astrocalc
</span></span><span class="line"><span class="cl">Cloning into 'github.com/spiegel-im-spiegel/astrocalc'...
</span></span><span class="line"><span class="cl">remote: Counting objects: 43, done.
</span></span><span class="line"><span class="cl">remote: Total 43 (delta 0), reused 0 (delta 0), pack-reused 43
</span></span><span class="line"><span class="cl">Unpacking objects: 100% (43/43), done.
</span></span><span class="line"><span class="cl">Checking connectivity... done.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo\src>popd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\GBDEMO
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> ├─github.com
</span></span><span class="line"><span class="cl"> │ └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl"> │ └─astrocalc
</span></span><span class="line"><span class="cl"> │ │ .editorconfig
</span></span><span class="line"><span class="cl"> │ │ .gitignore
</span></span><span class="line"><span class="cl"> │ │ .travis.yml
</span></span><span class="line"><span class="cl"> │ │ LICENSE
</span></span><span class="line"><span class="cl"> │ │ README.md
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │ └─modjulian
</span></span><span class="line"><span class="cl"> │ example_test.go
</span></span><span class="line"><span class="cl"> │ LICENSE
</span></span><span class="line"><span class="cl"> │ modjulian.go
</span></span><span class="line"><span class="cl"> │ modjulian_test.go
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> julian-day.go
</span></span></code></pre></div><p>この状態でビルドを実行してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb build
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl">julian-day
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>tree /f .
</span></span><span class="line"><span class="cl">C:\WORKSPACE\GBDEMO
</span></span><span class="line"><span class="cl">├─bin
</span></span><span class="line"><span class="cl">│ julian-day.exe
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">├─pkg
</span></span><span class="line"><span class="cl">│ └─windows-amd64
</span></span><span class="line"><span class="cl">│ │ julian-day.a
</span></span><span class="line"><span class="cl">│ │
</span></span><span class="line"><span class="cl">│ └─github.com
</span></span><span class="line"><span class="cl">│ └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl">│ └─astrocalc
</span></span><span class="line"><span class="cl">│ modjulian.a
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">└─src
</span></span><span class="line"><span class="cl"> ├─github.com
</span></span><span class="line"><span class="cl"> │ └─spiegel-im-spiegel
</span></span><span class="line"><span class="cl"> │ └─astrocalc
</span></span><span class="line"><span class="cl"> │ │ .editorconfig
</span></span><span class="line"><span class="cl"> │ │ .gitignore
</span></span><span class="line"><span class="cl"> │ │ .travis.yml
</span></span><span class="line"><span class="cl"> │ │ LICENSE
</span></span><span class="line"><span class="cl"> │ │ README.md
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │ └─modjulian
</span></span><span class="line"><span class="cl"> │ example_test.go
</span></span><span class="line"><span class="cl"> │ LICENSE
</span></span><span class="line"><span class="cl"> │ modjulian.go
</span></span><span class="line"><span class="cl"> │ modjulian_test.go
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └─julian-day
</span></span><span class="line"><span class="cl"> julian-day.go
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>bin\julian-day.exe 2015 1 1
</span></span><span class="line"><span class="cl">2015-01-01 00:00:00 +0000 UTC
</span></span><span class="line"><span class="cl">MJD = 57023日
</span></span></code></pre></div><p><a href="http://getgb.io/" title="gb - A project based build tool for Go">gb</a> ではプロジェクト・フォルダ以下にあるパッケージを自動で検索してビルドしてくれる。
もちろんパッケージを指定してビルドすることも可能。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb build github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl">github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">C:\workspace\gbdemo>gb build julian-day
</span></span><span class="line"><span class="cl">julian-day
</span></span></code></pre></div><p>さらにテストもできる<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C:\workspace\gbdemo>gb test -v github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl">=== RUN TestDayNumber
</span></span><span class="line"><span class="cl">--- PASS: TestDayNumber (0.00s)
</span></span><span class="line"><span class="cl">=== RUN ExampleDayNumber
</span></span><span class="line"><span class="cl">--- PASS: ExampleDayNumber (0.00s)
</span></span><span class="line"><span class="cl">PASS
</span></span></code></pre></div><p>パッケージによっては <code>go test</code> の結果と <code>gb test</code> の結果が異なる場合があるので注意が必要。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://qiita.com/shinofara/items/ac0591fef95c2c6e936e">golang - gbを知ったのでgojiを使ったWEBアプリケーションプロジェクトを管理してみた - Qiita</a></li>
<li><a href="http://deeeet.com/writing/2015/06/26/golang-dependency-vendoring/">Go言語のDependency/Vendoringの問題と今後.gbあるいはGo1.5 | SOTA</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>コマンド <code>go get</code> の使い方については「<a href="https://text.baldanders.info/golang/go-get-package/">go get コマンドでパッケージを管理する</a>」を参照のこと。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>テストについては「<a href="https://text.baldanders.info/golang/testing/">Go 言語のテスト・フレームワーク</a>」を参照のこと。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Go 言語のテスト・フレームワーク
tag:text.Baldanders.info,2015-09-19:/golang/testing/
2015-09-19T14:40:43+00:00
2019-07-01T13:48:09+00:00
パッケージ化したのならテストをしましょう。
Spiegel
https://baldanders.info/profile/
<p>(初出: <a href="http://qiita.com/spiegel-im-spiegel/items/64224f22ef17d916dc2d">はじめての Go 言語 (on Windows) その7 - Qiita</a>)</p>
<p><a href="https://text.baldanders.info/golang/packaging/" title="機能のパッケージ化">前回</a>の続き。</p>
<h2>テストコードを書く</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では最初からテスト・フレームワークが同梱されています。
いまどきの言語はみんなそうですよね。
テストコードを書くには対象のソースファイルと同じフォルダに <code>*_test.go</code> という名前のファイルを用意します。
まぁ,説明するより書いた方が早いですね。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">modjulian</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">mjdnTest</span> <span class="kd">struct</span> <span class="p">{</span> <span class="c1">//test case for DayNumber
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">in</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span> <span class="c1">//input data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">out</span> <span class="kt">int64</span> <span class="c1">//expected result
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">mjdnTests</span> <span class="p">[]</span><span class="nx">mjdnTest</span> <span class="c1">//test cases for DayNumber
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestMain</span><span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">M</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//initialization
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mjdnTests</span> <span class="p">=</span> <span class="p">[]</span><span class="nx">mjdnTest</span> <span class="p">{</span> <span class="c1">//test cases for DayNumber
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">{</span><span class="nx">time</span><span class="p">.</span><span class="nf">Date</span><span class="p">(</span><span class="mi">1969</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">31</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">UTC</span><span class="p">),</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">40586</span><span class="p">)},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">time</span><span class="p">.</span><span class="nf">Date</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">UTC</span><span class="p">),</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">40587</span><span class="p">)},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">time</span><span class="p">.</span><span class="nf">Date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">UTC</span><span class="p">),</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">57023</span><span class="p">)},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//start test
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">code</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//termination
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="nx">code</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestModifiedJulianDayNumber</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">testCase</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">mjdnTests</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nf">DayNumber</span><span class="p">(</span><span class="nx">testCase</span><span class="p">.</span><span class="nx">in</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">result</span> <span class="o">!=</span> <span class="nx">testCase</span><span class="p">.</span><span class="nx">out</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"DayNumber of \"%v\" = %d, want %d."</span><span class="p">,</span> <span class="nx">testCase</span><span class="p">.</span><span class="nx">in</span><span class="p">,</span> <span class="nx">result</span><span class="p">,</span> <span class="nx">testCase</span><span class="p">.</span><span class="nx">out</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol>
<li><code>package</code> にはテスト対象のパッケージを指定します。</li>
<li><code>import</code> には <a href="http://golang.org/pkg/testing/"><code>testing</code></a> パッケージを含めます。</li>
<li><code>Test...</code> で始まる関数名がテスト実行用の関数です。引数には <code>t *testing.T</code> を指定します。</li>
<li><code>TestMain()</code> は特別な関数です。テストの最初に呼び出され, <code>Run()</code> で他のテスト関数群をキックします。引数には <code>m *testing.M</code> を指定します。 <code>TestMain()</code> 内で初期化や条件を変えたテストの繰り返しや後始末処理などを行うことができます。</li>
</ol>
<p><a href="http://golang.org/pkg/testing/"><code>testing</code></a> パッケージには,他の言語のテスト・フレームワークによくある <a href="http://golang.jp/go_faq#assertions">assertion 関数がありません</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。 <a href="http://golang.jp/go_faq#testing_framework">FAQ</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> によると</p>
<blockquote>
<p>一般的なテストフレームワークにおいて条件・制御・出力機構を持つ専用のミニ言語が用意される傾向がありますが、Go言語にはすでにこれらが備わっています。これらを再び作成するより、我々はGo言語のテストを進めたかったのです。このようにしたことで余計な言語を覚える必要がなくなり、テストを直接的かつ理解しやすくしています。</p>
</blockquote>
<p>とあります。
テスト駆動型開発の場合,テストコードはそれ自体が設計書として機能しますので,この割り切りは妥当と言えます<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。
その代わりテストコードを(ドキュメントとして)きちんと書くのは骨が折れますが(笑)</p>
<p>テストコードが書けたので早速動かしてみましょう。
環境は<a href="https://text.baldanders.info/golang/packaging/" title="機能のパッケージ化">前回</a>の最後の状態をそのまま引き継ぎます。</p>
<p>テストを行うには <code>go test</code> コマンドを使います。
以下の例ではパッケージを指定していますが, <code>./...</code> と指定すれば全てのパッケージのテストが対象になります。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">C:<span class="se">\w</span>orkspace<span class="se">\j</span>d>go <span class="nb">test</span> -v github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestDayNumber
</span></span><span class="line"><span class="cl">--- PASS: TestDayNumber <span class="o">(</span>0.00s<span class="o">)</span>
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok github.com/spiegel-im-spiegel/astrocalc/modjulian 0.229s
</span></span></code></pre></div><p>これは成功例。じゃあ,<a href="https://github.com/spiegel-im-spiegel/astrocalc/blob/master/modjulian/modjulian.go">元のコード</a>を少しいじってわざと失敗させてみましょうか(なんだかなぁ)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">C:<span class="se">\w</span>orkspace<span class="se">\j</span>d>go <span class="nb">test</span> -v github.com/spiegel-im-spiegel/astrocalc/modjulian
</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestDayNumber
</span></span><span class="line"><span class="cl">--- FAIL: TestDayNumber <span class="o">(</span>0.00s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> modjulian_test.go:35: DayNumber of <span class="s2">"1969-12-31 00:00:00 +0000 UTC"</span> <span class="o">=</span> 40587, want 40586.
</span></span><span class="line"><span class="cl">FAIL
</span></span><span class="line"><span class="cl"><span class="nb">exit</span> status <span class="m">1</span>
</span></span><span class="line"><span class="cl">FAIL github.com/spiegel-im-spiegel/astrocalc/modjulian 1.566s
</span></span></code></pre></div><p>エラーレポートを吐く <code>Errorf()</code> は内部で <code>Fail()</code> を呼び出し,テスト自体は続行します。
一方 <code>Errorf()</code> の代わりに <code>Fatalf()</code> を使うと,内部で <code>FailNow()</code> を呼び出しテストを中断します。</p>
<p>Go 言語のテスト・フレームワークでは benchmark や coverage もサポートしてますが,今回は割愛します。</p>
<h2>テストの自動化(Continuous Integration)</h2>
<p>今回のコードは自動化するほどの規模でもないですが,話のついでに <a href="https://travis-ci.org/" title="Travis CI - Test and Deploy Your Code with Confidence">Travis CI</a> で自動化しちゃいましょう。
えっと,今回は <a href="https://travis-ci.org/" title="Travis CI - Test and Deploy Your Code with Confidence">Travis CI</a> の説明は割愛します。
興味のある方は「<a href="#bookmark">ブックマーク</a>」の項を参考にして下さい。</p>
<p><a href="https://travis-ci.org/" title="Travis CI - Test and Deploy Your Code with Confidence">Travis CI</a> でビルド・テストを行うためには <code>.travis.yml</code> を書く必要がありますが,テストを行うだけなら <code>.travis.yml</code> の記述は簡単です。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l">go</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">go</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="m">1.4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="m">1.5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v ./...</span><span class="w">
</span></span></span></code></pre></div><p>実行結果は<a href="https://travis-ci.org/spiegel-im-spiegel/astrocalc">ここ</a>を参照して下さい。</p>
<p><a href="https://text.baldanders.info/golang/document/" title="Go 言語のドキュメント・フレームワーク">次回</a>はドキュメントの話。</p>
<h2 id="bookmark">ブックマーク</h2>
<ul>
<li><a href="http://qiita.com/Jxck_/items/8717a5982547cfa54ebc">Go の Test に対する考え方 - Qiita</a></li>
<li><a href="http://straitwalk.hatenablog.com/entry/2014/09/18/232810">Goでテストを書く - 成らぬは人の為さぬなりけり</a></li>
<li><a href="http://qiita.com/umisama/items/0d589cca7e89b89c29a8">golang 1.4で追加されたtestingの便利機能(テストの初期化とお片づけ) - Qiita</a></li>
<li><a href="http://qiita.com/dmnlk/items/3fb4e0abb98e39fee275">Go + Travis CI + Coveralls でCI環境を作る - Qiita</a></li>
<li><a href="http://sue445.hatenablog.com/entry/2013/06/01/170607">GithubにあるリポジトリをTravis CI連携する手順 #junitbook - くりにっき</a></li>
<li><a href="http://deeeet.com/writing/2014/10/16/golang-in-ci-as-a-service/">CI-as-a-ServiceでGo言語プロジェクトの最新ビルドを継続的に提供する | SOTA</a></li>
<li><a href="http://uchimanajet7.hatenablog.com/entry/2015/03/20/211352">golangでTravis CIを使ってクロスコンパイルするときにハマったところ #golang #travisci - uchimanajet7のメモ</a></li>
<li><a href="http://qiita.com/atotto/items/b796c31c1755dbec13db">Go言語のビルド生活を drone.ioで幸せに暮らす #golang - Qiita</a></li>
<li><a href="https://developers.eure.jp/tech/go1_7-subtests/">Golang におけるサブテストの並行処理実装について | eureka tech blog</a></li>
<li><a href="https://qiita.com/tmzkysk/items/8bb37795ac223664d682">golangのテストはじめ - Qiita</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>オリジナルは <a href="http://golang-jp.org/doc/faq#assertions">http://golang-jp.org/doc/faq#assertions</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>オリジナルは <a href="http://golang-jp.org/doc/faq#testing_framework">http://golang-jp.org/doc/faq#testing_framework</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>私は組み込みエンジニアなので,プログラミングで assert を多用するのは,エンジニアの怠慢だと思ってしまいます。まぁ,ベクタ・テーブルからゴリゴリ書くってのなら別ですが。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>