<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <title>mojavy.com</title>
    <link>http://mojavy.com/blog</link>
    <description></description>
    <pubDate>Thu, 18 Oct 2012 18:00:01 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>iOSでZeroMQを試す</title>
      <link>http://mojavy.com/blog/2012/10/18/objective-c-zeromq/</link>
      <pubDate>Thu, 18 Oct 2012 18:00:01 JST</pubDate>
      <category><![CDATA[objective-c]]></category>
      <category><![CDATA[ios]]></category>
      <category><![CDATA[zeromq]]></category>
      <category><![CDATA[common lisp]]></category>
      <guid isPermaLink="true">http://mojavy.com/blog/2012/10/18/objective-c-zeromq/</guid>
      <description>iOSでZeroMQを試す</description>
      <content:encoded><![CDATA[<p><img alt="zeromq" src="/images/zeromq-logo.png" /></p>
<p><a href="http://mojavy.com/blog/2012/10/17/common-lisp-zeromq/">前回Common LispでZeroMQを試してみた</a>が、今度はiOSからも試してみた。<a href="http://www.zeromq.org/area:licensing">ZeroMQのライセンス</a>はLGPLだが、static linking exceptionがあるのでiPhoneアプリに組み込んでもソースは公開しなくて大丈夫なはず。(勘違いしてたらごめんなさい)</p>
<h1 id="zeromqiphone">ZeroMQをiPhone用にビルド</h1>
<p>ZeroMQはautotoolsで作成されておりconfigure &amp;&amp; make でビルドできるようになっているが、iPhone用にクロスコンパイルをするためには適切なオプションを与えてやる必要がある。</p>
<p>一応、<a href="http://www.zeromq.org/build:iphone">本家サイト上にもドキュメント</a>はあるが最新のXcodeではうごかない。また、シミュレータ用でもうごかせるようにしておきたい。というわけで、以下のようなビルドスクリプトを書いた。</p>
<script src="https://gist.github.com/3899653.js"> </script>

<p>これをZeroMQのtarを展開してできたディレクトリにbuild.shとかで保存して実行すると、armv7とi386に対応したlibzmq.aができる。</p>
<div class="pygments_borland"><pre><span class="nv">$ </span>tar xzf zeromq-2.2.0.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>zeromq-2.2.0
<span class="nv">$ </span>./build.sh
<span class="nv">$ </span>file libzmq.a
libzmq.a: Mach-O universal binary with 2 architectures
libzmq.a <span class="o">(</span><span class="k">for </span>architecture armv7<span class="o">)</span>:      current ar archive random library
libzmq.a <span class="o">(</span><span class="k">for </span>architecture i386<span class="o">)</span>:       current ar archive random library
</pre></div>

<p>このlibzmq.aと、includeディレクトリの中身をXcodeにインポートしてやればよい。
このときに、XcodeでOther Linker Flags に -lstdc++ を追加してやるのを忘れないように。</p>
<h1 id="zeromq">ZeroMQを用いた簡易チャットアプリ</h1>
<p>サンプルとしてチャットアプリを実装してみる。
チャットでの発言はREQ/REPを用いてアプリ→サーバに渡し、PUB/SUBを用いてサーバ→アプリにブロードキャストする。</p>
<h2 id="_1">サーバ側</h2>
<p>サーバ側はCommon Lispで実装した。単に、rep-sockから発言を受け取ってpub-sockにそのまま流すだけ。</p>
<div class="pygments_borland"><pre>(load &quot;~/.sbclrc&quot;)
(ql:quickload :zeromq)

(defun pull-and-publish ()
  (zmq:with-context (ctx 1)
    (zmq:with-socket (rep-sock ctx zmq:rep)
      (zmq:bind rep-sock &quot;tcp://127.0.0.1:5555&quot;)

      (zmq:with-context (ctx2 1)
        (zmq:with-socket (pub-sock ctx2 zmq:pub)
          (zmq:bind pub-sock &quot;tcp://127.0.0.1:5556&quot;)

          (loop
             (let ((msg (make-instance &#39;zmq:msg)))
               (zmq:recv rep-sock msg)
               (zmq:send rep-sock (make-instance &#39;zmq:msg :data &quot;ok&quot;))
               (zmq:send pub-sock msg))))))))

(pull-and-publish)
</pre></div>

<p>REQ/REPではなくPULL/PUSHでもできるはずだが、なぜかcl-zmqではpullがつかえなかったのでREQ/REPをつかった。</p>
<h2 id="_2">アプリ側</h2>
<p><a href="https://github.com/jeremy-w/objc-zmq">objective-c版ZeroMQ</a>もあるけど、今回はそのままCのAPIを利用した。
以下はソースの抜粋。</p>
<div class="pygments_borland"><pre><span class="cp">#import &quot;ChatViewController.h&quot;</span>
<span class="cp">#import &quot;include/zmq.h&quot;</span>

<span class="k">@interface</span> <span class="nc">ChatViewController</span> <span class="p">()</span> <span class="p">{</span>

    <span class="kt">void</span> <span class="o">*</span><span class="n">ctx1</span><span class="p">,</span> <span class="o">*</span><span class="n">ctx2</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">subsock</span><span class="p">,</span> <span class="o">*</span><span class="n">reqsock</span><span class="p">;</span>

    <span class="n">NSMutableArray</span> <span class="o">*</span><span class="n">messageList</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">@end</span>

<span class="err">@</span><span class="n">implementation</span> <span class="n">ChatViewController</span>

<span class="k">@synthesize</span> <span class="n">nickname</span><span class="p">;</span>
<span class="k">@synthesize</span> <span class="n">timer</span><span class="p">;</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">viewDidLoad</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">super</span> <span class="n">viewDidLoad</span><span class="p">];</span>

    <span class="n">ctx1</span> <span class="o">=</span> <span class="n">zmq_init</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="n">subsock</span> <span class="o">=</span> <span class="n">zmq_socket</span><span class="p">(</span><span class="n">ctx1</span><span class="p">,</span> <span class="n">ZMQ_SUB</span><span class="p">);</span>
    <span class="n">zmq_setsockopt</span><span class="p">(</span><span class="n">subsock</span><span class="p">,</span> <span class="n">ZMQ_SUBSCRIBE</span><span class="p">,</span> <span class="s">&quot;&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="n">zmq_connect</span><span class="p">(</span><span class="n">subsock</span><span class="p">,</span> <span class="s">&quot;tcp://127.0.0.1:5556&quot;</span><span class="p">);</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>

    <span class="n">ctx2</span> <span class="o">=</span> <span class="n">zmq_init</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="n">reqsock</span> <span class="o">=</span> <span class="n">zmq_socket</span><span class="p">(</span><span class="n">ctx1</span><span class="p">,</span> <span class="n">ZMQ_REQ</span><span class="p">);</span>
    <span class="n">rc</span> <span class="o">=</span> <span class="n">zmq_connect</span><span class="p">(</span><span class="n">reqsock</span><span class="p">,</span> <span class="s">&quot;tcp://127.0.0.1:5555&quot;</span><span class="p">);</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>

    <span class="n">self</span><span class="p">.</span><span class="n">timer</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSTimer</span> <span class="nl">scheduledTimerWithTimeInterval:</span><span class="mi">1</span> <span class="nl">target:</span><span class="n">self</span> <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">observeSocket</span><span class="p">)</span> <span class="nl">userInfo:</span><span class="nb">nil</span> <span class="nl">repeats:</span><span class="n">YES</span><span class="p">];</span>
    <span class="n">messageList</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSMutableArray</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">viewWillDisappear:</span><span class="p">(</span><span class="kt">BOOL</span><span class="p">)</span><span class="nv">animated</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">timer</span> <span class="n">invalidate</span><span class="p">];</span>
    <span class="n">zmq_close</span><span class="p">(</span><span class="n">subsock</span><span class="p">);</span>
    <span class="n">zmq_close</span><span class="p">(</span><span class="n">reqsock</span><span class="p">);</span>
    <span class="n">zmq_term</span><span class="p">(</span><span class="n">ctx1</span><span class="p">);</span>
    <span class="n">zmq_term</span><span class="p">(</span><span class="n">ctx2</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">observeSocket</span>
<span class="p">{</span>
    <span class="n">zmq_msg_t</span> <span class="n">msg</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="n">zmq_msg_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">);</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">do</span> <span class="p">{</span>
        <span class="n">rc</span> <span class="o">=</span> <span class="n">zmq_recv</span><span class="p">(</span><span class="n">subsock</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="n">ZMQ_NOBLOCK</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">EAGAIN</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;no data available&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">ENOTSUP</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;ENOTSUP&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">EFSM</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;EFSM&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">ETERM</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;ETERM&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">ENOTSOCK</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;ENOTSOCK&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">EINTR</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;EINTR&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="n">EFAULT</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;EFAULT&quot;</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">size_t</span> <span class="n">siz</span> <span class="o">=</span> <span class="n">zmq_msg_size</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">);</span>
            <span class="kt">void</span> <span class="o">*</span><span class="n">dat</span> <span class="o">=</span> <span class="n">zmq_msg_data</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">);</span>
            <span class="n">NSString</span> <span class="o">*</span><span class="n">str</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSString</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithData:</span><span class="p">[</span><span class="n">NSData</span> <span class="nl">dataWithBytes:</span><span class="n">dat</span> <span class="nl">length:</span><span class="n">siz</span><span class="p">]</span> <span class="nl">encoding:</span><span class="n">NSUTF8StringEncoding</span><span class="p">];</span>
            <span class="p">[</span><span class="n">messageList</span> <span class="nl">insertObject:</span><span class="n">str</span> <span class="nl">atIndex:</span><span class="mi">0</span><span class="p">];</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;unknown&quot;</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">zmq_msg_close</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">);</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">tableView</span> <span class="n">reloadData</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">IBAction</span><span class="p">)</span> <span class="nf">saySomething:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nv">sender</span>
<span class="p">{</span>
    <span class="n">UIAlertView</span> <span class="o">*</span><span class="n">alert</span> <span class="o">=</span> <span class="p">[[</span><span class="n">UIAlertView</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTitle:</span><span class="s">@&quot;text input&quot;</span>
                                <span class="nl">message:</span><span class="s">@&quot;&quot;</span>
                               <span class="nl">delegate:</span><span class="n">self</span>
                      <span class="nl">cancelButtonTitle:</span><span class="s">@&quot;cancel&quot;</span>
                      <span class="nl">otherButtonTitles:</span><span class="s">@&quot;ok&quot;</span><span class="p">,</span> <span class="nb">nil</span><span class="p">];</span>
    <span class="n">alert</span><span class="p">.</span><span class="n">alertViewStyle</span> <span class="o">=</span> <span class="n">UIAlertViewStylePlainTextInput</span><span class="p">;</span>
    <span class="p">[</span><span class="n">alert</span> <span class="n">show</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">alertView:</span><span class="p">(</span><span class="n">UIAlertView</span><span class="o">*</span><span class="p">)</span><span class="nv">alertView</span> <span class="nf">clickedButtonAtIndex:</span><span class="p">(</span><span class="n">NSInteger</span><span class="p">)</span><span class="nv">buttonIndex</span>
<span class="p">{</span>
    <span class="n">zmq_msg_t</span> <span class="n">msg</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">rc</span><span class="p">;</span>
    <span class="n">NSData</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span>
    <span class="n">NSString</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">buttonIndex</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="mi">0</span><span class="o">:</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="mi">1</span><span class="o">:</span>
            <span class="n">str</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSString</span> <span class="nl">stringWithFormat:</span><span class="s">@&quot;%@: %@&quot;</span><span class="p">,</span> <span class="n">nickname</span><span class="p">,</span> <span class="p">[</span><span class="n">alertView</span> <span class="nl">textFieldAtIndex:</span><span class="mi">0</span><span class="p">].</span><span class="n">text</span><span class="p">];</span>
            <span class="n">data</span> <span class="o">=</span> <span class="p">[</span><span class="n">str</span> <span class="nl">dataUsingEncoding:</span><span class="n">NSUTF8StringEncoding</span><span class="p">];</span>
            <span class="n">rc</span> <span class="o">=</span> <span class="n">zmq_msg_init_size</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="p">[</span><span class="n">data</span> <span class="n">length</span><span class="p">]);</span>
            <span class="n">assert</span><span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
            <span class="n">memcpy</span><span class="p">(</span><span class="n">zmq_msg_data</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">),</span> <span class="p">[</span><span class="n">data</span> <span class="n">bytes</span><span class="p">],</span> <span class="p">[</span><span class="n">data</span> <span class="n">length</span><span class="p">]);</span>

            <span class="n">zmq_send</span><span class="p">(</span><span class="n">reqsock</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
            <span class="n">zmq_recv</span><span class="p">(</span><span class="n">reqsock</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
            <span class="n">zmq_msg_close</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">default</span><span class="o">:</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">@end</span>
</pre></div>

<p>以下補足</p>
<ul>
<li>簡単のためタイマーで1秒ごとにソケットにメッセージがあるかどうかobserveSocketでチェックしているが、別スレッドにしたほうがスマート</li>
<li><a href="http://api.zeromq.org/2-2:zmq-recv">zmq_recvのマニュアル</a>によると、zmq_recvをZMQ_NOBLOCKで呼んだときにメッセージがなかった場合はEAGAINが返るとなっているが、-1がかえってきていた。</li>
<li>上記ソースはUITableViewControllerで発言を表示する想定になっているが、UIまわりのコードは省略</li>
</ul>
<h2 id="common-lisp">オマケ(Common Lisp版コマンドライン用チャットクライアント)</h2>
<div class="pygments_borland"><pre>(load &quot;~/.sbclrc&quot;)
(ql:quickload :zeromq)
(ql:quickload :bordeaux-threads)

(defun sub ()
  (zmq:with-context (ctx 1)
    (zmq:with-socket (socket ctx zmq:sub)
      (zmq:setsockopt socket zmq:subscribe &quot;&quot;)
      (zmq:connect socket &quot;tcp://127.0.0.1:5556&quot;)
      (loop
         (let ((query (make-instance &#39;zmq:msg)))
           (zmq:recv socket query)
           (format t &quot;received message: ~a~%&quot; (zmq:msg-data-as-string query)))))))

(defun client ()
  (zmq:with-context (ctx 1)
    (zmq:with-socket (socket ctx zmq:req)
      (zmq:connect socket &quot;tcp://127.0.0.1:5555&quot;)
      (loop
         (zmq:send socket (make-instance &#39;zmq:msg
                                         :data (read-line)))
         (let ((result (make-instance &#39;zmq:msg)))
           (zmq:recv socket result))))))

(bordeaux-threads:make-thread #&#39;sub)
(client)
</pre></div>

<h1 id="_3">まとめ</h1>
<ul>
<li>iPhoneでZeroMQを動かしてみた</li>
<li>ZeroMQのREQ/REPとPUB/SUBを使用してチャットをつくってみた</li>
<li>appleの審査を通過するかどうかは知らない(一応実績はあるらしい)</li>
</ul>
<h1 id="_4">参考</h1>
<ul>
<li><a href="http://api.zeromq.org/2-2:_start">http://api.zeromq.org/2-2:_start</a></li>
<li><a href="http://www.zeromq.org/area:licensing">http://www.zeromq.org/area:licensing</a></li>
<li><a href="http://www.zeromq.org/intro:commercial-support">http://www.zeromq.org/intro:commercial-support</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>jenkinsでiosアプリをビルドしてwifi経由で実機インストールする</title>
      <link>http://mojavy.com/blog/2012/08/07/jenkins-ios-build/</link>
      <pubDate>Tue, 07 Aug 2012 15:30:00 JST</pubDate>
      <category><![CDATA[jenkins]]></category>
      <category><![CDATA[ios]]></category>
      <guid isPermaLink="true">http://mojavy.com/blog/2012/08/07/jenkins-ios-build/</guid>
      <description>jenkinsでiosアプリをビルドしてwifi経由で実機インストールする</description>
      <content:encoded><![CDATA[<p><img alt="jenkins" src="/images/jenkins-logo.png" /></p>
<p>はまりどころが多くて大変だったので今後同じことをやろうとする人のために一連の作業をメモしておきます。</p>
<h2 id="_1">目的</h2>
<ul>
<li>jenkinsでiOSアプリをビルドする</li>
<li>ビルドしたアプリは、UDIDの登録なしに実機インストールして確認してもらいたい</li>
</ul>
<h2 id="_2">手順</h2>
<h3 id="ios-developer-enterprise-program">iOS Developer Enterprise Program への参加</h3>
<p>実機インストールするだけなら通常のデベロッパーアカウントでも大丈夫ですが、UDIDの登録なしにwifiから配布するためにはエンタープライズプログラムに参加する必要があります。</p>
<p>エンタープライズプログラムに参加すればapp storeを経由せずに自由にインストールできるようになりますが、アクセスできるのが特定の組織内に限定されるように適切に制限をかけなければ規約違反になります。</p>
<p><a href="https://developer.apple.com/jp/programs/ios/enterprise/">https://developer.apple.com/jp/programs/ios/enterprise/</a></p>
<h3 id="xcode">xcodeの設定</h3>
<p><a href="http://golog.plus.vc/iphone/3355/">http://golog.plus.vc/iphone/3355/</a> などを参考に、xcodeからprovisioning profileを設定します。</p>
<p>また、最近のxcodeではデフォルトではarmv7用バイナリしかビルドしないので、armv6用もビルドするように設定しておきます。
Build SettingsのArchitectures に 'armv6' を手動で追記すれば大丈夫です。</p>
<h3 id="_3">実機確認</h3>
<p>jenkinsでビルドする際は後述のビルドスクリプトを使うのですが、provisioning profileの設定等が正しくされていることを確認するために、一旦ここでxcodeのUI上で作成したipaファイルをiPhone構成ユーティリティを使って実機インストールしてみます。
xcodeでipaファイルをビルドする方法はすぐにでてくると思うので、適宜調べて下さい。</p>
<p>ここで正常にインストールできなければ、provisioning profile等の状態を確認してください。</p>
<h3 id="jenkins">jenkinsのセットアップ</h3>
<p>jenkinsを用意します。ビルド専用macが用意できない場合でも、開発に用いているmacをJNLP経由でそのままスレーブとして使うことができます。</p>
<p>jenkins自体の細かいセットアップ方法は割愛しますが、JNLP経由でスレーブを起動する場合は以下のようなスクリプトをバックグランドで起動するようにしておくと便利です。</p>
<div class="pygments_borland"><pre><span class="c">#!/bin/sh</span>

<span class="nb">eval</span> <span class="sb">`</span>ssh-agent<span class="sb">`</span>
ssh-add ~/.ssh/id_rsa
java -jar slave.jar -jnlpUrl http://jenkins.example.com/computer/mac1/slave-agent.jnlp
</pre></div>

<p>ここでの注意点は以下のとおりです。</p>
<ul>
<li>gitやsvnにssh経由でアクセスする場合は、パスワード無しでチェックアウトできるように適切に設定しておく必要がある。(上記の場合はssh-agentをあらかじめ起動するようにして、パスワード入力は起動時のみになるようにしています。)</li>
<li>keychainに配布に使用するデベロッパーアカウントの証明書を登録しておく。</li>
</ul>
<h3 id="_4">ビルドスクリプトの作成</h3>
<p>以下のようなmakefileをxcodeプロジェクトのトップディレクトリに配置します。
(お好みのビルドツールを利用してください)</p>
<p>ソースコードのコンパイルはxcodebuild, ipaファイルの生成はxcrunというコマンドを使います。</p>
<div class="pygments_borland"><pre><span class="nv">APP_NAME</span> <span class="o">=</span> MyApp
<span class="nv">SRCTOP</span> <span class="o">=</span> <span class="k">$(</span>shell <span class="nb">pwd</span><span class="k">)</span>
<span class="nv">RELEASE_BUILD_DIR</span> <span class="o">=</span> <span class="k">$(</span>SRCTOP<span class="k">)</span>/build/Release-iphoneos
<span class="nv">BUILD_HISTORY_DIR</span> <span class="o">=</span> <span class="k">$(</span>SRCTOP<span class="k">)</span>/build
<span class="nv">DEVELOPER_NAME</span> <span class="o">=</span> <span class="s2">&quot;iPhone Distribution: XXXX.&quot;</span>
<span class="nv">PROVISIONING_PROFILE</span> <span class="o">=</span> path/to/in_house.mobileprovision

all:: dist
.PHONY: clean compile dist

dist: compile
    /usr/bin/xcrun -sdk iphoneos PackageApplication -v <span class="k">${</span><span class="nv">RELEASE_BUILD_DIR</span><span class="k">}</span>/<span class="k">${</span><span class="nv">APP_NAME</span><span class="k">}</span>.app -o <span class="k">${</span><span class="nv">BUILD_HISTORY_DIR</span><span class="k">}</span>/<span class="k">${</span><span class="nv">APP_NAME</span><span class="k">}</span>.ipa --sign <span class="k">${</span><span class="nv">DEVELOPER_NAME</span><span class="k">}</span> --embed <span class="k">${</span><span class="nv">PROVISIONING_PROFILE</span><span class="k">}</span>
    ./gen_dist_page.sh

compile:
    xcodebuild

clean:
    rm -rf build
</pre></div>

<p>配布用のplistとhtmlを生成するスクリプト(gen_dist_page.sh)は以下のようになります。(BUILD_NUMBERなどの変数はjenkinsの環境変数から取得できます)</p>
<div class="pygments_borland"><pre><span class="nv">PACKAGE_NAME</span><span class="o">=</span>MyApp

<span class="nv">PLIST</span><span class="o">=</span>build/<span class="k">${</span><span class="nv">PACKAGE_NAME</span><span class="k">}</span>.plist
<span class="nv">INSTALL_PAGE</span><span class="o">=</span>build/<span class="k">${</span><span class="nv">PACKAGE_NAME</span><span class="k">}</span>.html

cat <span class="s">&lt;&lt;__PLIST__ &gt; $PLIST</span>
<span class="s">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
<span class="s">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;</span>
<span class="s">&lt;plist version=&quot;1.0&quot;&gt;</span>
<span class="s">  &lt;dict&gt;</span>
<span class="s">    &lt;key&gt;items&lt;/key&gt;</span>
<span class="s">    &lt;array&gt;</span>
<span class="s">      &lt;dict&gt;</span>
<span class="s">        &lt;key&gt;assets&lt;/key&gt;</span>
<span class="s">        &lt;array&gt;</span>
<span class="s">          &lt;dict&gt;</span>
<span class="s">            &lt;key&gt;kind&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;software-package&lt;/string&gt;</span>
<span class="s">            &lt;key&gt;url&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;${DIST_URL}/build/${PACKAGE_NAME}.ipa&lt;/string&gt;</span>
<span class="s">          &lt;/dict&gt;</span>
<span class="s">          &lt;dict&gt;</span>
<span class="s">            &lt;key&gt;kind&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;display-image&lt;/string&gt;</span>
<span class="s">            &lt;key&gt;needs-shine&lt;/key&gt;</span>
<span class="s">            &lt;true/&gt;</span>
<span class="s">            &lt;key&gt;url&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;http://jenkins.example.com/job/myapp/ws/icon.png&lt;/string&gt;</span>
<span class="s">          &lt;/dict&gt;</span>
<span class="s">        &lt;/array&gt;</span>
<span class="s">        &lt;key&gt;metadata&lt;/key&gt;</span>
<span class="s">        &lt;dict&gt;</span>
<span class="s">          &lt;key&gt;bundle-identifier&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;com.example.myapp.MyApp&lt;/string&gt;</span>
<span class="s">          &lt;key&gt;kind&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;software&lt;/string&gt;</span>
<span class="s">          &lt;key&gt;subtitle&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;b${BUILD_NUMBER}&lt;/string&gt;</span>
<span class="s">          &lt;key&gt;title&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;${PACKAGE_NAME}&lt;/string&gt;</span>
<span class="s">        &lt;/dict&gt;</span>
<span class="s">      &lt;/dict&gt;</span>
<span class="s">    &lt;/array&gt;</span>
<span class="s">  &lt;/dict&gt;</span>
<span class="s">&lt;/plist&gt;</span>
<span class="s">__PLIST__</span>

cat <span class="s">&lt;&lt;__HTML__ &gt; $INSTALL_PAGE</span>
<span class="s">&lt;html&gt;</span>
<span class="s">  &lt;head&gt;</span>
<span class="s">    &lt;title&gt;${PACKAGE_NAME}: build #$BUILD_NUMBER&lt;/title&gt;</span>
<span class="s">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0&quot;&gt;</span>
<span class="s">    &lt;/head&gt;</span>
<span class="s">    &lt;body&gt;</span>
<span class="s">    &lt;div style=&quot;text-align: center; vertical-align: middle; margin-top: 100px&quot;&gt;</span>
<span class="s">      &lt;div style=&quot;padding: 10px;&quot;&gt;$JOB_NAME&lt;/div&gt;</span>
<span class="s">      &lt;a href=&quot;itms-services://?action=download-manifest&amp;url=${DIST_URL}/${PLIST}&quot;</span>
<span class="s">         style=&quot;border: none; text-decoration: none; color: #888;&quot;&gt;</span>
<span class="s">        &lt;img src=&quot;http://jenkins.example.com/job/myapp/ws/demo.png&quot;/&gt;</span>
<span class="s">        &lt;br /&gt;&lt;br /&gt;</span>
<span class="s">        Install ${PACKAGE_NAME} b$BUILD_NUMBER</span>
<span class="s">      &lt;/a&gt;</span>
<span class="s">    &lt;/div&gt;</span>
<span class="s">  &lt;/body&gt;</span>
<span class="s">&lt;/html&gt;</span>
<span class="s">__HTML__</span>
</pre></div>

<p>htmlはリンク先さえあっていれば大丈夫なのですが、plistの方はいくつか注意が必要です。</p>
<ul>
<li>display-imageは必須。iOSのバージョンによってはなくてもよいようなのですが、iOS 5.1.1だとdisplay-imageがplistにないと「Appをダウンロードできません」というエラーになります。</li>
<li>bundle-identifierはxcodeのBundle Identifierと一致させる。</li>
</ul>
<p>ここではxcodeデフォルトのsdkバージョンを使用していますが、必要に応じてxcodebuild等のコマンドラインオプションを追加してください。</p>
<p>参考:  <a href="https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf">https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf</a></p>
<h3 id="jenkinsjob">jenkinsのjob作成</h3>
<p>通常通りjenkinsのjobを作ります。適切にソースコードをチェックアウトして、ビルドフェーズで上記スクリプトを実行するようにします。</p>
<div class="pygments_borland"><pre>make clean <span class="o">&amp;&amp;</span> make <span class="nv">DIST_URL</span><span class="o">=</span><span class="k">${</span><span class="nv">BUILD_URL</span><span class="k">}</span>artifact
</pre></div>

<p>ビルドに成功するとipa, html, plistファイルが生成されるので、これらを成果物として保存します。
成果物のhtmlページにiPhoneでアクセスしてみて、アプリがインストールできれば成功です。</p>
<h2 id="_5">その他</h2>
<p>今回は触れませんでしたが、以下についても考慮しておいたほうがよいと思います。</p>
<ul>
<li>iOSのSDKバージョン</li>
<li>依存ライブラリ</li>
<li>プロビジョニングプロファイルの管理</li>
</ul>
<p>また、<a href="https://testflightapp.com/">TestFlight</a> というサービスもあるのでそちらの利用も検討してみてもいいと思います。</p>
<h2 id="_6">まとめ</h2>
<p>iOSアプリをコマンドラインからビルドするスクリプトを作成して、jenkins上でビルドから配布までを行なうための手順を紹介しました。</p>
<h2 id="_7">参考</h2>
<ul>
<li><a href="https://developer.apple.com/jp/programs/ios/enterprise/">https://developer.apple.com/jp/programs/ios/enterprise/</a></li>
<li><a href="http://www.apple.com/jp/support/iphone/enterprise/">http://www.apple.com/jp/support/iphone/enterprise/</a></li>
<li><a href="https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf">https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf</a></li>
<li><a href="http://support.apple.com/kb/DL1465?viewlocale=ja_JP">http://support.apple.com/kb/DL1465?viewlocale=ja_JP</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>xcodeのiosエミュレータのコンソールログはsystem.logに吐かれる</title>
      <link>http://mojavy.com/blog/2012/07/25/ios-simulator-log/</link>
      <pubDate>Wed, 25 Jul 2012 20:30:00 JST</pubDate>
      <category><![CDATA[ios]]></category>
      <guid isPermaLink="true">http://mojavy.com/blog/2012/07/25/ios-simulator-log/</guid>
      <description>xcodeのiosエミュレータのコンソールログはsystem.logに吐かれる</description>
      <content:encoded><![CDATA[<p><img alt="ios" src="/images/ios-logo.png" /></p>
<p>タイトルの通りです。</p>
<p>iosエミュレータで標準出力に書いたものはsystem.logに吐かれます。</p>
<p>ちなみに最近のxcodeのiosプロジェクトは以下のようなコマンドでシンタックスチェックができます。</p>
<div class="pygments_borland"><pre><span class="nv">$ </span>clang -Wall -Wextra -Wno-unused-parameter -fsyntax-only  -miphoneos-version-min<span class="o">=</span>4.3 -xobjective-c -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk *.m
</pre></div>

<p>参考：　<a href="http://sakito.jp/emacs/emacsobjectivec.html">http://sakito.jp/emacs/emacsobjectivec.html</a></p>]]></content:encoded>
    </item>
  </channel>
</rss>
