- 投稿日:2020-09-19T23:56:08+09:00
Electronアプリをビルド・発行する時のプラットフォームにおける制約とネイティブモジュールとの関係
Electronを用いるとクロスプラットフォームな開発ができることは皆さんご存じの通りです。
しかし、ビルド→発行としていくなかで、それらの作業がすべて一つのプラットフォーム上で完結するというわけではありません。
また、ネイティブモジュールを利用する場合は、話が異なります。
そこらへんについて説明していこうと思います。
一つのプラットフォームですべてのプラットフォームにたいしてビルド・発行できるか
例えば「WindowsですべてのOS向けのアプリをビルドできるか?」という問題ですが、ネイティブモジュールの利用有無次第となります。
ネイティブモジュールを利用していない場合
ビルドはできます。
ただし、Mac向けの発行はCode Signingが必要になるため、Mac上でしかできません。
よって、Mac上で動かせば、ビルドから発行まですべてMac上で完結します。
Docker イメージ
electron-builderを動かせる環境として、Dockerイメージが用意されています。
しかし、これはLinux上で動かすのと同じなので、WindowsとLinux向けのアプリのビルド・発行しかできません。
また、Windows用のネイティブモジュールを利用する場合は、ビルドもできません。
ネイティブモジュールについては、次の節で説明します。
ネイティブモジュールを利用している場合
ネイティブモジュールは各OS向けにそれぞれビルドする必要があります。
よって、基本的にはターゲットとなるプラットフォームの数だけビルド環境を用意する必要があります。
Macのネイティブモジュールをビルドするなら、Mac。
WindowsならWindowsのビルド環境が必要です。
昨今は、Mac用のVMも用意されていることを考えると、用意することは難しいことではないかもしれませんが、開発に当たっては実機が必要になることに間違いはないでしょう。
ネイティブモジュールを使わずとも、多くの機能が実装できることは確かですが、いつかはぶち当たる問題になります。
必要になったらで良いとは思いますが、ネイティブモジュールとそれに伴うビルド・発行環境の用意を検討する必要があるでしょう。
- 投稿日:2020-09-19T21:06:22+09:00
便利なコマンドメモ 【随時更新】
便利なコマンドのメモです。
一度、使うと使い続けることも多いですが、たまに使うだけのコマンドも少々見受けられるので、ここにメモすることにします。
動作環境はMac
としますが、そのままLinux
でも動く場合が多いはずです。現在のディレクトリから再帰的に文字列を検索する。
ripgrep もしくは ripgrep-all を使います。
ripgrep はデフォルトで
.gitignore
に記述されているパターンを無視します。
ripgrep-all は ripgrep のラッパーでdocx
,sqlite
,jpg
,zip
,tar.*
,movie subtitles (mkv, mp4)
なども検索対象に含めます。以下は、Apple Tutorial: Start Developing iOS Apps (Swift) で作成したアプリのルートディレクトリを検索した場合になります。
$ rg meal FoodTracker/Info.plist 64: <string>Allows you to add photos to your meals.</string> FoodTracker/Meal.swift 25: static let ArchiveURL = DocumentsDirectory.appendingPathComponent("meals") FoodTracker/MealViewController.swift 22: or constructed as part of adding a new meal. 24: var meal: Meal? 33: if let meal = meal { 34: navigationItem.title = meal.name 35: nameTextField.text = meal.name 36: photoImageView.image = meal.photo 37: ratingControl.rating = meal.rating 89: // Set the meal to be passed to MealTableViewController after the unwind segue. 90: meal = Meal(name: name, photo: photo, rating: rating) ... たくさんあるので省略します。現在のディレクトリから再帰的にファイルを検索する。
fd を使います。
fd はデフォルトで.gitignore
に記述されているパターンを無視します。かんたんな検索
例えば、https://github.com/oreilly-japan/deep-learning-from-scratch のリポジトリをクローンしてリポジトリを落としたフォルダに
cd
したあと、simple
が含まれるファイルを探したい場合は以下のようになります。$ fd simple ch01/simple_graph.py ch04/gradient_simplenet.py ch07/simple_convnet.py
正規表現を使った検索
以下は
正規表現
を使った例です。$ fd '^gradient.*py$' ch04/gradient_1d.py ch04/gradient_2d.py ch04/gradient_method.py ch04/gradient_simplenet.py ch05/gradient_check.py ch07/gradient_check.py common/gradient.pyルートディレクトリの指定
fd
の2番目の引数
に検索したいディレクトリを指定することができます。$ fd layer common common/layers.py common/multi_layer_net.py common/multi_layer_net_extend.py
引数なしで実行
fd
は引数なしでも実行できます。ls -R
に似た動作です。$ fd LICENSE.md README.md ch01 ch01/hungry.py ch01/img_show.py ch01/man.py ch01/simple_graph.py ch01/sin_cos_graph.py ch01/sin_graph.py ch02 ch02/and_gate.py ch02/nand_gate.py ... たくさんあるので省略します。現在のディレクトリ下にある複数のファイルを対象に(再帰的に)リネームする。
rename は
Linux OS
には基本的にデフォルトで存在しますが、Mac OS
の場合は、別途インストールする必要があります。$ brew install rename基本的な使い方としては以下のようになります。
$ rename -s <from> <to> [<files>]名前は同じでも別物のようですが、用途としては十分に役割を果たしてくれます。
以下の例は
icon-nxn.png
ファイルをfavicon-nxn.png
とリネームするコマンドです。
n
には 16 だとか 32 だとかの整数が入ります。$ fd -g "icon-*.png" -x rename -s icon faviconfd の
-x <cmd>
は コマンドを並列実行、-X <cmd>
はまとめて1回だけ実行となります。
なので上の例に関しては-X
オプションでも可能です。また、
-g
オプションはglob パターン
を有効にします。デフォルトでは正規表現が有効です。シンボリックリンクを作成する。
これもよく忘れるのでメモります。
たとえば、/home/src
という名前のシンボリックリンクを作成し、/usr/src
を指すようにしたい場合は、以下のように入力します。$ ln -s /usr/src /home/srcシンボリックリンクを削除する。
rm
もしくはunlink
コマンドを使用します。$ rm <link-name> $ unlink <link-name>
- 参考
Mac で tmux とクリップボードを共有する。
tmux
はvi モード
で tmux-sensible がインストールされていることを前提としています。
.tmux.conf
に以下の記述を追加します。.tmux.confunbind -t vi-copy Enter bind-key -t vi-copy Enter copy-pipe "pbcopy"すると、
Prefix + [
でコピーモードに入り、Space
で選択を開始、Enter
で選択部分のコピーができる。
vi
っぽくしたいなら以下のように記述すればいいです。.tmux.confbind-key -t vi-copy v begin-selection bind-key -t vi-copy y copy-pipe "pbcopy"
Mac
やtmux
のバージョンによってreattach-to-user-namespace
の設定が必要だったり、そうじゃなかったりするようです。
- 投稿日:2020-09-19T20:51:00+09:00
Git基本事項
用語(編集途中)
コミット
ユーザーが任意のタイミングで記録を保存する操作.
リポジトリ
コミットを貯めておく場所.
すでにGitで管理されているプロジェクトの開発に参加する場合はリポジトリをコピー(クローン)して使う.
ワークツリー
変更するファイルを保持する場所.
ステージングエリア
コミットするファイルを登録する場所.
Gitディレクトリ
コミットを格納する場所.
GitHubにあげるまでの手順
今回はGitのインストールやGitHubのアカウント作成などの初期設定の説明は省く.
あらかじめ登録してある人が新しいプロジェクトを始めるという状況を想定.
リモートリポジトリをローカルへ複製する or ローカルで作成したリポジトリをリモートへ反映させる
今回はリモート側のリポジトリとして先にGitHubへリポジトリを作成し,それをローカルへ複製するという方法で進める.
$ git clone GitHubのリポジトリURLGitHubのファイルが複製されるので cd コマンドでコピーしたディレクトリに移動する.
作業用ブランチを作成する
Gitではbranchを作成し作業を行い,完成したらmasterにmergeするのが基本なのでここでbranchを作成する.
以下のコマンドで現在いるbranchが確認できる.
$ git branch * masterブランチの作成には以下のコマンドを使う.
$ git branch ブランチ名例)
$ git branch workブランチを作成したら以下のコマンドでブランチを切り替える
$ git checkout ブランチ名例)
$ git checkout workブランチを切り替えたらファイルの編集を行い,バージョンアップを行う.
コミットをする
書き換えたファイルはリモートリポジトリに保存しておきたいので以下のコマンドを実行する.$ git add ファイル名このコマンドで指定したファイルがコミット対象になる.
今回はフォルダにあるファイルを全てコミットする予定なので以下のコマンドを実行する.
$ git add .これでコミットの準備ができたので以下のコマンドでコミットを行う.
$ git commit -m 'コメント’これでコミットは完了.(-mを付けなかった場合はvi上でコメントの入力を求められる.)
ブランチをマージする
今回はmasterにworkを取り込む. まずはmasterブランチに移動する.$ git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'.このまま編集したファイルを開いてもブランチを切り替えているので変更が反映されていない.
例)
$ git merge work Updating ac63b89..3eefb35 Fast-forward README.md | 1 + 1 file changed, 1 insertion(+)このコマンドでマージが成功すると編集した内容がmasterに反映される.
リモートにpushをする
いよいよ変更内容をGItHubに反映させる.
例)
$ git push origin work Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 338 bytes | 338.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), completed with 1 local object. remote: remote: Create a pull request for 'work' on GitHub by visiting: remote: GitHubのURL remote: To GitHubのURL * [new branch] work -> workこれでGitHubにちゃんと反映されていたら成功!
おさらい
大事なのは以下のコマンドでこの流れは絶対である.$ git clone URL $ git add . $ git commit -m 'コメント' $ git push origin master知っていると便利なコマンド
$ git statusこのコマンドで今の状態を確認できる.
慣れるまでは何かを実行するたびにこのコマンドで確認した方がいいかも.
$ git branchこのコマンドで現在どのブランチにいるのかを確認できる.
不十分な点
- 開発はGitHub→Gitという流れよりローカル→リモートという流れが自然?
- ブランチ切ったままファイルの移動をしてしまった場合どうなっちゃう?
- 後からフォルダに違うディレクトリ にあったファイルを持ってきた場合どうやったらコミット対象になってくれるのか
- 投稿日:2020-09-19T17:17:14+09:00
夏の終わりに彼女と河川敷で線香花火する予定だったのに、雨だったのと元々彼女居ないせいで出来なかったので諦めてプログラムで作ってみた。
はじめに
タイトルは、、、お察し下さい。。。
openFrameworksというメディアアート用のフレームワークを使って、
線香花火を作りました。
gifだと小さくてちょっと地味ですね。。。
せっかく作ったので、もうちょっと見やすいやつをyoutubeに上げる予定です。環境は下記になります。
macOS Catalina 10.15.6
Xcode 12.0
openframeworks v0.11.0 osx作る準備
線香花火の火の粉の飛び方
下記の動画を参考にさせてもらいました。
「線香花火 パチパチの仕組みを解明」
https://www.youtube.com/watch?v=ZwqxrJumCe4火種から小さい火の粉が飛んでその火の粉からまた小さい火の粉が飛んで、、、みたいな感じです。
openFrameworksを用意する
下記の公式ページでライブラリが一式zipダウンロードできます。
https://openframeworks.cczip解凍してprojectGeneratorというソフトが内蔵されてるので、
そのツールを使って、プロジェクト作成します。
作成したプロジェクトの中の.xcodeprojファイルをxcodeで開くだけで準備できました!
ちなみに、サンプルもかなり高性能なものが入っていて、xcodeでビルドすれば動かせるので、楽しめます。コード
コードの全体もここに載せておきます。
個人的に大事だと思うことだけ説明します。・下記関数はフレームごとに呼び出されて、点の位置をランダムに配置します。
app_update・下記関数はopenFrameworksの関数です。
ofApp::setup() ・・・最初に一回だけ呼び出される。
ofApp::update() ・・・毎フレーム呼び出される。(更新処理)
ofApp::draw() ・・・毎フレーム呼び出される。(描画処理)
ofApp::keyPressed ・・・キー押下ごとによびだされる・それぞれの点を木構造で管理したかったので、下記のクラスを作りました。
class myNode・MACRO_DEBUG_MODEのdefineを有効にしてビルドすると、デバッグ情報が画面に表示されて、キー押下ごとにフレームが進むようになってます。それはそれでちょっとそれっぽい感じになります。
ofApp.cpp#include "ofApp.h" #include "myNode.hpp" // #define MACRO_DEBUG_MODE #define MAX_NODE_NUM 1000 #define MAX_NODE 16 #define EDA_1_RAD ofRandom(180, 220) #define EDA_2_RAD ofRandom(60, 70) #define EDA_3_RAD ofRandom(30, 35) #define EDA_4_RAD ofRandom(15, 15) using namespace std; /* class defienitoin */ template <class X_CLS> class myNode { myNode<X_CLS> * parent ; myNode<X_CLS> * child[MAX_NODE] ; int child_cnt = 0; int depth = 0; public: X_CLS val; myNode(); myNode(myNode<X_CLS> * parent); void setParent(myNode<X_CLS> * parent); void setChild(myNode<X_CLS> * child); void setDepth(int p_depth); myNode<X_CLS> * getParent(); int getDepth(); void clear(); /* ~myNode(); */ }; template <class X_CLS> myNode<X_CLS>::myNode() { } template <class X_CLS> myNode<X_CLS>::myNode(myNode<X_CLS> * parent) { this->parent = parent; } template <class X_CLS> void myNode<X_CLS>::setParent(myNode<X_CLS> * parent) { this->parent = parent; } template <class X_CLS> myNode<X_CLS> * myNode<X_CLS>::getParent() { return this->parent; } template <class X_CLS> void myNode<X_CLS>::setChild(myNode<X_CLS> * child) { this->child[child_cnt] = child; child_cnt++; } template <class X_CLS> void myNode<X_CLS>::setDepth(int p_depth) { this->depth = p_depth; } template <class X_CLS> int myNode<X_CLS>::getDepth() { return this->depth; } template <class X_CLS> void myNode<X_CLS>::clear() { this->parent = nullptr; for (int i = 0 ; i < child_cnt; i++) { this->child[i] = nullptr; } child_cnt = 0; } /* function definition */ float getPointXCncntcircle (float cntr_x, float rad, float theta) { return (rad * cos(theta)) + cntr_x; } float getPointYCncntcircle (float cntr_y, float rad, float theta) { return (rad * sin(theta)) + cntr_y; } /* global variant */ ofVec2f base_pos; array<myNode<class ofVec2f>, MAX_NODE_NUM> lsNode; int phase = 0; int i_node_end = 0; long frameCnt = 0; void app_update(){ int i_node = 0; int node_1_cnt = ofRandom(1, 5); int node_2_cnt = ofRandom(1, 5); int node_3_cnt = ofRandom(1, 5); int node_4_cnt = ofRandom(1, 5); int node_1_st = 0; int node_2_st = 0; int node_3_st = 0; int node_4_st = 0; frameCnt++; if (frameCnt <= 100) { phase = 1; } else if (frameCnt <= 200) { phase = 2; } else if (frameCnt <= 300) { phase = 3; } else if (frameCnt <= 400) { phase = 4; } else if (frameCnt <= 500) { phase = 0; frameCnt = 0; } /* ノードクリア */ for (int i = 0; i < i_node_end; i++) { lsNode[i].clear(); } i_node_end = 0; if (phase == 0) { return ; } /* 枝分かれ1 **/ node_1_st = i_node; for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++,i_node++) { lsNode[i_node].setDepth(1); lsNode[i_node].val.x = getPointXCncntcircle( base_pos.x, EDA_1_RAD, glm::radians(ofRandom(0, 359)) ); lsNode[i_node].val.y = getPointYCncntcircle( base_pos.y, EDA_1_RAD, glm::radians(ofRandom(0, 359)) ); } i_node_end = i_node; if (phase == 4) { return ; } /* 枝分かれ2 **/ node_2_st = i_node; for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++) { for (int i_node_2 = 0; i_node_2 < node_2_cnt; i_node_2++,i_node++) { lsNode[i_node].setDepth(2); lsNode[node_1_st + i_node_1].setChild(&lsNode[i_node]); lsNode[i_node].setParent(&lsNode[node_1_st + i_node_1]); lsNode[i_node].val.x = getPointXCncntcircle( lsNode[i_node].getParent()->val.x, EDA_2_RAD, glm::radians(ofRandom(0, 359)) ); lsNode[i_node].val.y = getPointYCncntcircle( lsNode[i_node].getParent()->val.y, EDA_2_RAD, glm::radians(ofRandom(0, 359)) ); } } i_node_end = i_node; if (phase == 3) { return ; } /* 枝分かれ3 **/ node_3_st = i_node; for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++) { for (int i_node_2 = 0; i_node_2 < node_2_cnt; i_node_2++) { for (int i_node_3 = 0; i_node_3 < node_3_cnt; i_node_3++,i_node++) { lsNode[i_node].setDepth(3); lsNode[node_2_st + (i_node_1 * node_2_cnt) + i_node_2].setChild(&lsNode[i_node]); lsNode[i_node].setParent(&lsNode[node_2_st + (i_node_1 * node_2_cnt) + i_node_2]); lsNode[i_node].val.x = getPointXCncntcircle( lsNode[i_node].getParent()->val.x, EDA_3_RAD, glm::radians(ofRandom(0, 359)) ); lsNode[i_node].val.y = getPointYCncntcircle( lsNode[i_node].getParent()->val.y, EDA_3_RAD, glm::radians(ofRandom(0, 359)) ); } } } i_node_end = i_node; if (phase == 2) { return ; } /* 枝分かれ4 **/ node_4_st = i_node; for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++) { for (int i_node_2 = 0; i_node_2 < node_2_cnt; i_node_2++) { for (int i_node_3 = 0; i_node_3 < node_3_cnt; i_node_3++) { for (int i_node_4 = 0; i_node_4 < node_4_cnt; i_node_4++,i_node++) { lsNode[i_node].setDepth(4); lsNode[node_3_st + (i_node_1*node_2_cnt*node_3_cnt) + (i_node_2*node_3_cnt) + i_node_3].setChild(&lsNode[i_node]); lsNode[i_node].setParent(&lsNode[node_3_st + (i_node_1*node_2_cnt*node_3_cnt) + (i_node_2*node_3_cnt) + i_node_3]); lsNode[i_node].val.x = getPointXCncntcircle( lsNode[i_node].getParent()->val.x, EDA_4_RAD, glm::radians(ofRandom(0, 359)) ); lsNode[i_node].val.y = getPointYCncntcircle( lsNode[i_node].getParent()->val.y, EDA_4_RAD, glm::radians(ofRandom(0, 359)) ); } } } } i_node_end = i_node; } void setFireColor(){ int rand = ofRandom(0, 7); switch (rand) { case 1: ofSetHexColor(0xff4500); break; case 2: ofSetHexColor(0xfffacd); break; case 3: ofSetHexColor(0xff6347); break; case 4: ofSetHexColor(0xff8c00); break; case 5: ofSetHexColor(0xffff00); break; default: ofSetHexColor(0xfa8072); break; } } //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(18); ofBackgroundHex(0x000000); base_pos.x = ofGetWidth()/2; base_pos.y = ofGetHeight()/2; } //-------------------------------------------------------------- void ofApp::update(){ #ifdef MACRO_DEBUG_MODE #else app_update(); #endif } //-------------------------------------------------------------- void ofApp::draw(){ setFireColor(); ofDrawCircle(base_pos.x, base_pos.y, 15); #ifdef MACRO_DEBUG_MODE ofDrawBitmapString("phase: " + ofToString(phase), 10, 10); ofDrawBitmapString("i_node_end: " + ofToString(i_node_end), 10, 20); #endif for (int i_node = 0; i_node < i_node_end; i_node++) { if (lsNode[i_node].getParent() != nullptr) { ofDrawLine( lsNode[i_node].getParent()->val.x, lsNode[i_node].getParent()->val.y, lsNode[i_node].val.x, lsNode[i_node].val.y ); } #ifdef MACRO_DEBUG_MODE ofDrawBitmapString("i: " + ofToString(i_node), 0, 30 + i_node*20); if (lsNode[i_node].getParent() != nullptr) { ofDrawBitmapString( "P.x: " + ofToString((int)lsNode[i_node].getParent()->val.x), 60, 30 + i_node*20); ofDrawBitmapString("P.y: " + ofToString((int)lsNode[i_node].getParent()->val.y), 120, 30 + i_node*20); } ofDrawBitmapString("v.x: " + ofToString((int)lsNode[i_node].val.x), 180, 30 + i_node*20); ofDrawBitmapString("v.y: " + ofToString((int)lsNode[i_node].val.y), 270, 30 + i_node*20); #endif } } //-------------------------------------------------------------- void ofApp::keyPressed(int key){ #ifdef MACRO_DEBUG_MODE cout << "===============" << endl; app_update(); #endif } //-------------------------------------------------------------- void ofApp::keyReleased(int key){ } //-------------------------------------------------------------- void ofApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void ofApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mousePressed(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseReleased(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseEntered(int x, int y){ } //-------------------------------------------------------------- void ofApp::mouseExited(int x, int y){ } //-------------------------------------------------------------- void ofApp::windowResized(int w, int h){ } //-------------------------------------------------------------- void ofApp::gotMessage(ofMessage msg){ } //-------------------------------------------------------------- void ofApp::dragEvent(ofDragInfo dragInfo){ }src配下に配置しているコードは他にもmain.cpp、ofApp.hがありますが、これらはopenFrameworksのprojectGeneratorで自動生成したものなので、記載は省略します。
おわりに
openFrameworks初めて使いましたが、作った物がグラフィックで見えるのがすごく楽しいと思いました。他にも機能が山ほどあるみたいなので、今度調べて使ってみたいと思います。
そして、いつの間にか夏も終わってしまいましたね。
秋も楽しみましょう。以上です。