woshidan's loose leaf

ぼんやり勉強しています

iOSアプリをxcodebuildでビルドする(途中)

Appiumの前段階として、Simulatorなどにインストールするビルド済みのAppファイル*1*2が必要です。

メモ

そういうわけで、ターミナルから xcodebuild を使って Appファイル の作成を試します。

$ xcodebuild
xcodebuild: error: The directory /Users/woshidan/path/ios-project/root/ does not contain an Xcode project.

$ cd path/to/XcodeProject

$ ls
TestApp         TestApp.xcodeproj       TestAppUITests          build

まずは、 XCodeで作成したプロジェクトのあるところ( .xcodeproj ファイルのあるディレクトリ ) へ移動します。

依存しているライブラリのクラスのヘッダが見つからない ...

xcworkspace など依存ライブラリの設定が入ったファイルを、 -workspace で指定しましょう。その際、 schema も指定が必要です。

// # CocoaPodsを使っていてxcworkspaceを指定してビルドする
$ xcodebuild -workspace ../../Test/TestLib.xcworkspace -scheme TestApp

CompileStoryboard TestApp/Base.lproj/Main.storyboard というメッセージが出てStoryBoardがビルドできない

Xcode のバージョンによっては、特定の設定がないとビルドができないものがあります。そう、古い設定は新しいXCodeのバージョンが出るたびにビルドできなくなる可能性があるのです*3

というわけでそういう場合は、xcodebuildで利用するXCodeのパスを古いものに xcode-select で切り替えます。

# xcode-select -s /Applications/__oldXcode.app/Contents/Developer

シミュレータでビルドしてもらうよう指定する

-sdk iphonesimulator オプションを追加してビルドします。

$ xcodebuild -workspace ../../Test/TestLib.xcworkspace -scheme TestApp -sdk iphonesimulator

のはずなんですが、めっちゃいっぱい ❌ /Applications/__oldXcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.3.sdk/usr/include/sys/_types.h:76:9: unknown type name '__uint32_t'; did you mean '__uint128_t'?

が出てつらいんで解決したらまたなんか書こう...。

*1:.appファイルはXCodeでビルドが完了すると $(PROJECT_DIR)/build/Release-iphoneos/xxxxx.app のようなパスに作成され、これを Payload フォルダに入れて圧縮 -> 拡張子を変更したものが .ipa ファイル https://developer.apple.com/jp/documentation/IDEs/Conceptual/AppDistributionGuide/TestingYouriOSApp/TestingYouriOSApp.html http://lain.heavy.jp/lain/2012/01/13/462 参照

*2:appiumのcapsファイルに指定するファイルは .ipa でも .app でもよさそう...?

*3:若干、いいすぎですが、メジャーバージョンが2~3離れたりしたら厳しい

今日のbash

& ... コマンドをバックグラウンドで実行する

参考: http://itpro.nikkeibp.co.jp/article/COLUMN/20060224/230589

$ heavy_command&

case 文 ... 他の言語のswitch と同じ

参考: http://shellscript.sunone.me/case.html

  casein
    パターン1 ) 処理1;;
    パターン2 ) 処理2 ;;
    パターン3 ) 処理3 ;;
    …
    パターンn ) 処理n ;;

) の記法がマジでびびる。

getopts

参考: https://www.ibm.com/support/knowledgecenter/ja/ssw_aix_71/com.ibm.aix.cmds2/getopts.htm https://qiita.com/b4b4r07/items/dcd6be0bb9c9185475bb

コマンド・ラインの引数を処理し、有効なオプションを検査する。

こないだrubyでちょいとしたスクリプト書いてる時に引数検査をARGVでeach回してぐるぐるしてたんですけど、 あったわ〜 シェルにあったわ〜 って感じです。

getopts OptionString Name [Argument...]

OptionString は、使用したいオプションの文字列。よくある例としては hhelp で使い方を出力する、といったもの。

: をつけるかどうかで振る舞いが変わり、 OptionString の後ろに : をつけると、そのオプションは引数を必要とするようになります。

指定していないオプションや引数を使った場合に

# https://qiita.com/b4b4r07/items/dcd6be0bb9c9185475bb より引用
./test.sh: option requires an argument -- d
./test.sh: illegal option -- x

のようにエラー表示が出て終了しますが、 OptionString の全体の前に : をつけるとこのエラー表示は行われなくなり、自前でのエラー処理を行うことができます。

https://qiita.com/b4b4r07/items/dcd6be0bb9c9185475bb の記事が非常に参考になり、 getopts のテンプレートなども紹介されているので今度便利コマンドを使う時は利用したいところです。

$! ... 最後に実行されたバックグラウンドコマンドのプロセスIDに展開

参考: https://qiita.com/laikuaut/items/1daa06900ad045d119b4

ちなみに現在動かしている(バックグラウンドでない)シェルのプロセス番号は $ で展開される。

$ echo $$
521

会社のrubyのバージョンが2.4.0以降にあげられないので `Array#-` と同等のスニペットをおいておく

irb(main):010:0>   class Array
irb(main):011:1>     def - other
irb(main):012:2>       copy = self.dup
irb(main):013:2>       other.each do |other_array_item|
irb(main):014:3*         copy.delete other_array_item
irb(main):015:3>       end
irb(main):016:2>       copy
irb(main):017:2>     end
irb(main):018:1>   end
=> :-
irb(main):019:0> a = [1, 2, 3]
=> [1, 2, 3]
irb(main):020:0> b = [1, 2, 4]
=> [1, 2, 4]
irb(main):021:0> a - b
=> [3]

参考

自作クラスのテスト用にComparableモジュールと宇宙船演算子を使って同じオブジェクトか比較しやすくする

rubyでちょっとした処理を担当する自作クラスのテストをするとき、同じオブジェクトがどうかの比較する際、

assert_equal expect_obj.hash_value, actual_obj.hash_value # hash_valueはオブジェクトのidではないオブジェクトに特有の値

というような式をよく書いていました。ですが、そんなに広く使われないクラスであることがわかっているなら == 演算子の挙動をいじってしまって、

assert_equal expect_obj, actual_obj

とかけるようにしちゃっていいかも、ということでその方法をメモ。

class MyClass
  include Comparable # 宇宙船演算子の内容を元に `<=`, `>=` を使えるようにしてくれる

  def <=> other # 宇宙船演算子を使って、`<`, `==`, `>` 用の動作を定義
    return nil unless other.is_a? MyClass
    hash_value <=> other.hash_value # MyClassのオブジェクトが同一かどうか判断するための比較に、hash_valueの比較を使う
  end

  def hash_value
    # テストでも作りやすい値を元にハッシュ値を計算する
  end
end

参考

Effective Ruby

Effective Ruby

rubyでライブラリなどを書くとき、Rakefileと相対パスのrequireを使ってクラスの取得やテストをやりやすくする

+---+ lib
|   +-+ some_module
|     +-+-- a_processor
|       |   +-- order.rb
|       |   +-- item.rb
|       +-- b_processor
|       |   +-- order.rb
|       |   +-- item.rb
|       +-- composer.rb # composerは a_processor, b_processor 以下のクラスを必要とする
+---+ test # ここから一括で回せるようにしたい
    +-+ some_module
      +-+-- a_processor
        |   +-- order_test.rb
        |   +-- item_test.rb
        +-- b_processor
        |   +-- order_test.rb
        |   +-- item_test.rb
        +-- composer_test.rb

といった構成のライブラリを作るとして、composer 書いたり、 地味にテストを自動実行できるようにどうしたら良いか調べるのがめんどくさかったのでメモ。

追加分

+---+ lib
|   +-+ some_module
|     +-+-- a_processor
|       |   +-- order.rb
|       |   +-- item.rb
|       +-- b_processor
|       |   +-- order.rb
|       |   +-- item.rb
|       +-- composer.rb
|       +-- a_processor.rb ** 追加
|       +-- b_processor.rb ** 追加
+---+ test
    + Rakefile ** 追加
    +-+ some_module
      +-+-- a_processor
        |   +-- order_test.rb
        |   +-- item_test.rb
        +-- b_processor
        |   +-- order_test.rb
        |   +-- item_test.rb
        +-- composer_test.rb

それっぽく他パッケージにあるクラスを全て読み込む

比較的苦しくない抑えどころに近いんですが、そのパッケージのクラスを丸ごと読み込んでくれるファイル用意して、それを require すると楽そう。

# a_processor.rb
require "#{File.dirname(__FILE__)}/a_processor/order.rb"
require "#{File.dirname(__FILE__)}/a_processor/item.rb"

ただ、明示的にクラスの指定どうこうする必要がない場合は、 Dir クラスなどを使って each で回すと良さそう。

# a_processor.rb をなくしてbuilder.rb 冒頭で
Dir.glob("#{File.dirname(__FILE__)}/a_processor/*").each do |file|
  require file
end

test以下のディレクトリのファイル全てをコマンド一つで実行する

今回は test ディレクトリ以下で rake test で全部実行できればよかったので、下記のような Rakefiletest 直下に置く。 Rakefile はおいたところで Rakefile に書いたタスクが rake xxx (xxxなしだと task :default に定義したものが使われる)実行されるようになるので、書いてから、普通はトップディレクトリにおいて、 ./**/*_test.rb -> ./test/**/*_test.rb にしたほうがいいかとも思った。

# Rakefile

# coding: UTF-8

require 'rake/testtask'

task :default => [:test]

Rake::TestTask.new do |test|
  test.test_files = Dir['./**/*_test.rb']
  test.verbose = false
end

なお、テストファイルで本番用のファイルを要求するときは、そのライブラリ用のベースクラス的なのを用意して、そこで require しておくと楽。

# base_test.rb
require 'test/unit'

Dir.glob("#{File.dirname(__FILE__)}/a_processor/*").each do |file|
  require file
end

Dir.glob("#{File.dirname(__FILE__)}/b_processor/*").each do |file|
  require file
end

Dir.glob("#{File.dirname(__FILE__)}/**.rb").each do |file|
  require file
end

class SomeModule::BaseTest < Test::Unit::TestCase
  include SomeModule

  ...

# 他のテスト用クラスは

require "#{File.dirname(__FILE__)}/path/to/base_test.rb"

class SomeModule::SomeTest < SomeModule::BaseTest

参考

gitのローカルリポジトリからお目当のタグを抽出する

今日は時間がないので渾身かも、と思ったコードに数行コメントつけてPOST!

  • シェルは便利なんだけどシェルの結果の文字列を気軽に処理したい時、ruby書いたことある人にとってはrubyがとても楽
  • rubyだとシェルのコマンドを %x( command ) などで実行できる
  • ruby xx.rb --params などのコマンドライン引数は ARGV に配列で入ってる
  • 一時的にディレクトリを移動したいだけなら Dir.chdir にブロック渡すのが便利
  • 他の人とディレクトリ構造が違う(ホームディレクトリなどは?) => ENV['HOME'] など ENV から取れるよ
  • git fetch remote-repo --tags で現在のステージの内容などを気にせずに git tag の出力だけ更新できるよ
    • git tag の出力は git FETCH_HEAD にある tag と一致していて*1、要するに git の管理対象のファイルの内容を読み書きしているわけではないので
    • 作業途中で同期のための雑な git stash しなくていいの嬉しい!
latest_tag = Dir.chdir("#{ENV['HOME']}/woshidan-test/sample-repo") do
  if fetch_remote
    puts "fetch tags from remote repository ..."
    %x( git fetch remote-repo --tags ) # ローカルリポジトリを最新にするコマンドを挟むことで自分を信じないことができる
    puts "done"
  end
  %x( git tag ).split("\n").select { |tag| tag.match(/お目当のタグの正規表現/) }.last
end

参考

*1:このファイルから出力されているわけではないだろうが

AWS CLIの設定ファイルにて名前付きプロファイルを利用して手軽にAWS CLIのIAMユーザを切り替える

普段業務でAWS CLIを使っているため、業務本番用のアカウントとちょっと個人の調査用のアカウントを分けて使いたいわけですが*1aws configure コマンドで設定できるアカウントは一つだけです。

これではちょっと調査をするたびにクレデンシャル情報を引っ張ってきて入力するのがめんどくさくなるのが目に見えているので他の方法を考えます。

aws configure コマンドで何をしているか

aws configure コマンドで何をしているかというか、 ~/.aws/config~/.aws/credentials にコマンドで与えた引数の入力を行なっています。

入力結果のファイルは下記のような形です。

~/.aws/config

[default]
region = ap-northeast-1

~/.aws/credentials

[default]
aws_access_key_id = ABCDEEGHEXAMPLEKEYID
aws_secret_access_key = ABCdefgHIjkLM/OPQrstUVwxyzAbcsExampleKey

このファイルに何か見出しつけてもう1組書いてやったらできそうですね。

awsの設定ファイルで名前付きプロファイルを使う

[XXX] のように見出しをつけて、認証情報などをその見出しごとに管理でき、これを 名前付きプロファイル と言うそうです。

~/.aws/config

[default]
region = ap-northeast-1

[profile user2] // credentialsと見出しが違うので注意
region=us-east-1
output=text // 出力形式も設定できる

~/.aws/credentials

[default]
aws_access_key_id = ABCDEEGHEXAMPLEKEYID
aws_secret_access_key = ABCdefgHIjkLM/OPQrstUVwxyzAbcsExampleKey

[user2]
aws_access_key_id=AKIAI44QH8DHBEXAMPLE
aws_secret_access_key=je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY

このように見出しをつけて、

$ aws ec2 describe-instances --profile user2

のように --profile オプションをつけて実行するか、

$ export AWS_PROFILE=user2

のように環境変数を設定することでaws cliで利用するユーザーアカウントを切り替えることができます。

現場からは以上です。

参考

docs.aws.amazon.com

*1:以前はできたらプライベートAWSの費用も持って欲しい気持ちがあったのですが、いまのところ月50円以下、いっても500円以内ということが判明したので請求する方がめんどくさい、ということでやめた