プログラムのバージョン管理とオープンソース
プログラムを複数人で開発する場合のバージョン管理と、オープンソースプログラムを使う場合の注意を説明する。
バージョン管理システム
プログラムを学校や自宅のパソコンで開発する場合、そのソースコードはどのように持ち運び管理修正すべきだろうか?
最も原始的な方法は、常に全部を持ち歩く方法かもしれない。しかし、プログラムが巨大になってくるとコピーに時間がかかる。またコピーを取る時に、どれが最新なのか正しく把握する必要がある。
- 同期方式 – 2つのディレクトリのファイルの古い日付のファイルを、新しい日付のファイルで上書きするようなディレクトリ同期ソフトを使って管理
- 圧縮保管 – ファイル全体だと容量も多いため、複数のファイルを1つのファイルにまとめて圧縮を行う tar コマンドを使うことも多い。(tar ball管理)
diffとpatch
プログラムの修正を記録し、必要最小限で修正箇所の情報を共有する方式に patch がある。これには、2つのファイルの差異を表示する diff コマンドの出力結果(通称patch)を用る。diff コマンドでは、変更のある場所の前後数行の差異を !(入替) +(追加) -(削除) の目印をつけて出力する。patch コマンドに diff の出力を与えると、!,+,- の情報を元に修正を加えることができる。(通称「patchをあてる」)
((( helloworld-old.c ))) #include <stdio.h> void main() { printf( "Hello World\n" ) ; } ((( helloworld.c ))) #include <stdio.h> int main( void ) { printf( "Hello World\n" ) ; return 0 ; } ((( diff の実行 ))) $ diff -c helloworld-old.c helloworld.c ((( 生成された patch 情報 ))) *** helloworld-old.c 2022-07-25 10:09:10.694442400 +0900 --- helloworld.c 2022-07-25 10:09:26.136433100 +0900 *************** *** 1,5 **** #include <stdio.h> ! void main() { printf( "Hello World\n" ) ; } --- 1,6 ---- #include <stdio.h> ! int main( void ) { printf( "Hello World\n" ) ; + return 0 ; }
インターネットの初期の頃には、他の人のプログラムに対して間違いを見つけると、作者に対してこのpatch(diff出力)をメールなどで送付し、プログラムの修正が行われた。
広く世界で使われている Web サーバ apache は、オープンソースで開発されてきた。当初はプログラム公開後に間違いや機能追加の情報(patch)が世界中のボランティア開発者から送られてきながら改良が加えられていった。このため、”a too many patches”「つぎはぎだらけ」という自虐的皮肉を込めて apache と名付けられたと言われている。
初期のバージョン管理システム
バージョン管理システムは、複数人で少しづつテキストファイルに修正を加えながら改良を行うような際に、誰がどのような修正を行ったかという修正履歴を管理するためのツール。unix などのプログラム管理では rcs (revision control system) が使われていたが、その改良版として cvs (concurrent version system) が使われるようになっていった。(現在は後に紹介する Git などが主流)
- ci コマンド(check in) – ファイルをバージョン管理の対象として登録する。
- co コマンド(check out) – ファイルを編集対象とする(必要に応じて書き込みロックなども可能)。co されたファイルは、編集した人が ci して戻すまで ci することができない。
- 修正結果を ci する際には、新しい編集のバージョン番号などをつけて保存される。
- co コマンドでは、バージョン番号を指定してファイルを取り出すことも可能。
[Bさんの修正] /check out \check in ファイルver1.0-----→ver1.1------→ver1.2 \check out /check in [Aさんの修正]
集中管理型バージョン管理システム
rcs,cvs では、ファイルのバージョンは各ファイルを対象としているため、ファイルやディレクトリの移動や削除は管理が困難であった。これらの問題を解決するために、集中管理を行うサーバを基点として、対象ファイルのディレクトリ全体(ソースツリー)に対してバージョン番号を振って管理を行う。subversion はサーバに ssh などのネットワークコマンドを介して、保存・改変を行うことができる。
しかし、複数の人の修正のマージ作業の処理効率が悪く、処理速度が遅いため使われなくなっていった。同様のバージョン管理システムが企業により有償開発されていた(BitKeeperなど)が製品のライセンス問題が発生し、業を煮やした Linux 開発の Linus が Git のベースを開発・公開している。
分散型バージョン管理システム
Gitは、プログラムのソースコードなどの変更履歴を記録・追跡するための分散型バージョン管理システムである。Linus によって開発され、ほかの多くのプロジェクトで採用されている。(以下wikipedia記事を抜粋加筆)
Gitは分散型のソースコード管理システムであるため、リモートサーバ等にある中心リポジトリの完全なコピーを手元(ローカル環境)に作成して、そのローカルリポジトリを使って作業を行う。
一般的な開発スタイルでは、大雑把に言えば、以下のようなステップの繰り返しで作業が行なわれる:
- git clone – リモートサーバ等にある中心リポジトリをローカルに複製する。
- git commit – ローカルでコンテンツの修正・追加・削除を行い、ローカルリポジトリに変更履歴を記録する。
- 必要に応じて過去の状態の閲覧や復元などを行う。場合によってはこのステップを何度か繰り返す。
- git push – ローカルの変更内容を中心リポジトリに反映させる。
- git merge – git push の段階で、作業者ごとの変更内容が衝突することもある。Gitが自動で解決できる場合もあれば、手動での解決する。
- git pull – 更新された中心リポジトリ(他者の作業内容も統合されている)をローカルの複製にも反映する。これによりローカル環境のコードも最新の内容になるので、改めてステップ2の作業を行う。
ローカルリポジトリ(Aさん) ver1.0a1 ver1.0a2 ver1.1a1 修正--(git commit)--修正--(git commit) 修正--(git commit) /git clone \git push /git pull Bさんの修正 中心リポジトリver1.0-----------------ver1.1 も含まれる \git clone /git push 修正--(git commit)--修正--(git commit) 編集の衝突が発生すると ver1.0b1 ver1.0b2 git merge が必要かも ローカルリポジトリ(Bさん)
GitHub
Git での中心リポジトリを保存・管理(ホスティング)するためのソフトウェア開発のプラットフォーム。コードの管理には Git を利用し GitHub 社によって保守されている。2018年よりマイクロソフトの傘下企業となっている。
GitHub では単なるホスティングだけでなく、プルリクエストやWiki機能(ドキュメントの編集・閲覧機能)といった、開発をスムーズに行うための機能も豊富である。(個人的な例:github.com/tohrusaitoh/)
GitHub で管理されているリポジトリには、公開リポジトリと非公開リポジトリがあり、非公開リポジトリはその管理者からの招待をうけないとリポジトリ改変に参加できない。
企業でのプログラム開発で GitHub を内々で使っている事例なども多いが、間違って公開リポジトリと設定されていて企業の開発中のプログラムが漏洩してしまった…との事例もあるので、企業での利用では注意が必要。
オープンソースとライセンス
オープンソースプログラムは、プログラムのソースコードをインターネットで公開されたものである。しかし、元となったプログラムの開発者がその利用に対していくつかの制約を決めていることが多い。これらのオープンソースプログラムでのソフトウェア開発手法の概念として「伽藍とバザール」を紹介する。
伽藍とバザール
伽藍(がらん)とは、優美で壮大な寺院のことであり、その設計・開発は、優れた設計・優れた技術者により作られた完璧な実装を意味している。バザールは有象無象の人の集まりの中で作られていくものを意味している。
たとえば、伽藍方式の代表格である Microsoft の製品は、優秀なプロダクトだが、中身の設計情報などを普通の人は見ることはできない。このため潜在的なバグが見つかりにくいと言われている。
これに対しバザール方式では明確な方針が決められないまま、インターネットで公開されているプログラムをボランティアを中心とした開発者を中心に開発していく手法である。
代表格の Linux は、インターネット上にソースコードが公開され、誰もがソースコードに触れプログラムを改良してもいい(オープンソース)。その中で、新しい便利な機能を追加しインターネットに公開されれば、良いコードは生き残り、悪いコードは自然淘汰されていく。このオープンソースを支えているツールとしては、前に述べた git が有名。
オープンソース・ライセンス
ソースコードを公開している開発者の多くは、ソフトウェア開発が公開することで発展することを期待する一方で、乱用をふせぐために何らかの制約をつけていることが多い。最初の頃は、開発者に敬意を示す意味で、プログラムのソースコードに開発者の名前を残すこと、プログラムを起動した時に開発者の名前が参照できること…といった条件の場合もあったが、最近ではソフトウェアが広く普及・発展することを願って条件をつけることも多い。
こういったオープンライセンスの元となったのは、Emacs(エディタ),gcc(コンパイラ)の開発者のストールマンであり、「ユーザーが自由にソフトウェアを実行し、(コピーや配布により)共有し、研究し、そして修正するための権利に基づいたソフトウェアを開発し提供することにより、ユーザーにそのような自由な権利を与えた上でコンピュータやコンピューティングデバイスの制御をユーザーに与えること」を目標に掲げた GNU プロジェクトがある。linux を触る際のコマンドで、g で始まるプログラムの多くは GNU プロジェクトのソフトウェア。
GNU プロジェクトが掲げる GNU ライセンス(GPL)では、GPLが適用されていれば、改良したソフトウェアはインターネットに公開する義務を引き継ぐ。オープンソースライセンスとして公開の義務の範囲の違いにより、BSD ライセンス、Apacheライセンスなどがある。
コピーレフト型 | GNU ライセンス(GPL) | 改変したソースコードは公開義務, 組み合わせて利用では対応箇所の開示が必要。 |
準コピーレフト型 | LGPL, Mozilla Public License | 改変したソースコードは公開義務。 |
非コピーレフト型 | BSDライセンス Apacheライセンス |
ソースコードを改変しても公開しなくてもいい。 |
GPLライセンス違反
GPLライセンスのソフトウェアを組み込んで製品を開発した場合に、ソースコード開示を行わないとGPL違反となる。大企業でこういったGPL違反が発生すると、大きな風評被害による損害をもたらす場合がある。
- SwitchBot 社製品のGPL違反の注意喚起 – といっても2年間放置されてたの?
- SwitchBot 社が、この2023年7月に、GPL違反の注意喚起を受け、ようやく対応したようだ
最近のライセンスが関連する話題を1つ紹介:GitHub を使った AI プログラミング機能「Copilot」というサービスが提供されている。Copilot のプラグインをインストールした vscode(エディタ) では、編集している関数名や変数名などの情報と GitHub で公開されているプログラムの 学習結果を使って、関数名を数文字タイプするだけで関数名・引数・処理内容などの候補を表示してくれる。しかし、Copilot を使うと非オープンライセンスで開発していたプログラムに、オープンソースのプログラムが紛れ込む可能性があり、非オープンソースプロジェクトが GPL で訴えられる可能性を心配し「Copilot は使うべきでない」という意見の開発者も出ている。Copilot だけでなく、生成系 AI によるプログラムでも、同様の問題が指摘されている。
理解度確認
IchigoJamとMapleSylup
7/25(木) 18:30~20:00
ボタンとLED点灯
IchigoJam には、LED 命令と BTN() 関数がある。BTN() 関数は、SW2 のボタンが押されていれば1,押されていなければ0を返す。
このため、ボタンを押している間 LED を点灯する関数は以下のように書ける。
10 LED BTN() 20 GOTO 10 RUN
これを踏まえ、ボタンが押された回数を数えるプログラムを作ってみよう。以下のプログラムは、BTN() が 1 の時、Aの値を増やし、Aの値を表示する。
10 A=0 20 IF BTN()=1 THEN A=A+1 : PRINT A 30 GOTO 20 RUN
しかし、このプログラムは1回ボタンを押すだけで、Aの値はいくつも増えるはずである。
これは、IchigoJam の処理が速いため、人間がボタンを1度押してボタンを離すまでの間に何度も繰り返すためである。
練習問題:ボタンを1回押したら1つづつ増えるようにプログラムを書いてみよう。(練習問題)
このプログラムであれば、ボタンが押されない間待ち、押されたらボタンが離れるまで待ち、1回とカウントする。(練習問題)
10 A = 0 20 IF BTN()=0 THEN GOTO 20 30 IF BTN()=1 THEN GOTO 30 40 A = A+1 50 PRINT A 60 GOTO 20
“でもこのプログラムでも「チャタリング」の影響で上手くカウントできない。どう直せばいいのだろうか?”と書いてチャタリングによる誤動作を見せるつもりだったけど、実際にやってみると、IchigoJam の処理速度の影響で、WAITなしでもチャタリングによる誤動作は発生しなかった。
チャタリングは数100μsec~数msecほど継続する可能性がある。このため「25 WAIT 1」と「35 WAIT 1」を追加すれば、問題ない。
複数のLEDの点灯
IchigoJam の定番の最初のプログラムでは、LED1,LED0 命令で LED を点灯させる実験をするが、OUT1~OUT4ポートを使って複数の LED を点灯させてみよう。
LED 1 2 3 4 OUT 1, 1 〇✕✕✕ 一つづつポートを1に変化 OUT 2, 1 〇〇✕✕ OUT 3, 1 〇〇〇✕ OUT 4, 1 〇〇〇〇 OUT 2, 0 〇✕〇〇 OUT 0 ✕✕✕✕ まとめて消す命令 OUT `1100 ✕✕〇〇 4ポートまとめて点灯/消灯 OUT `1010 ✕〇✕〇
(練習問題)
10 OUT `1100 : WAIT 20 20 OUT `0110 : WAIT 20 30 OUT `0011 : WAIT 20 40 OUT `0110 : WAIT 20 50 GOTO 10
10進数と2進数
OUT 命令で、複数ポートをまとめて変更する際には、「`1010」といったIchigoJam BASICでの2進数の取扱いの理解が必要なので、ちょっとだけ10進数,8進数,2進数の説明。
IchigoJam BASIC で2進数を扱う場合は、バッククオート` を使う。シングルクオート’ と間違いに注意。
10進数とは 123)10 = 1✕102 + 2✕10 + 3 のように、数字を10をひとまとめとして、10が10個で100を表すというように各桁を対応させたもの。
人間が8本指で進化したら、数字は8をひとまとめで扱い、123)8 = 1✕82 + 2✕8 + 3 = 83)10 として表現する8進数になったかもしれない。
コンピュータの中では、電気のある/なし の2通りで表現することで動いているため、各桁を0と1で表す 2進数 が便利。1010)2 = 1✕23 + 0✕22 + 1✕21 + 0 = 10)10
IchigoJam によるモータ制御の実験
IchigoJam で モータを動かす場合、MapleSylup というモータドライバボードを使うと簡単にモータが制御できる。
IchigoJam には、主要な出力ポートが OUT1~OUT6まである。このうち、OUT2~OUT5までは サーボモータの制御に使える。
(重要) IchigoJam の OUT ポートに直接「直流モーター」を接続してはいけない。モータを回すために大量の電流が使われるため IchigoJam は誤動作などを起こす可能性が高い。場合によってはモータから出るノイズで IchigoJam が壊れる可能性がある。必ず MapleSylup などのモータを動かす専用の回路を経由させること。
直流モータとサーボモータ
直流モータは、直流の電圧を加えることで回転するモータで、電圧を加える方向で右回転・左回転を簡単に制御できる。
サーボモータは、模型飛行機などの羽の角度などを変化させることに特化したモータで、中に直流モータと信号に応じて回転角度を変えるための電子回路が組み込まれているため、パルス幅信号PWM により簡単に角度を変化させることができる。
ただし、モーターは電流がたくさん流れるため、IchigoJam の電源からモータを回すと、IchigoJam を動かすための電力が不足して誤動作してしまう。このため、MapleSylup のモータドライバによって乾電池からの電力でモータを回す必要がある。
ポートの配線
IchigoJam の上に MapleSylup を接続して、MapleSylup のジャンパを配線する。
- OUT2ポートを、サーボモータではなく直流モータの配線につながるように、J2 を 2=3 接続となるようにジャンパをつなぐ。
- 同様に、J3 を 2=3 接続となるようにジャンパをつなぐ。
直流モータを動かす(OUT1,OUT2 , OUT5,OUT6)
MapleSylup では、今回 OUT1,OUT2 と OUT5,OUT6 の出力ポートにそれぞれ直流モータを接続する。OUT1,1 とOUT2,0 の時、OUT1側が V+, OUT2側が GND=0V になることでモータが回転する。OUT1,0 と OUT2,1 とすれば、OUT1側がGND=0V, OUT2側が V+ となることで、先ほどとは逆方向に回転する。
((( モータを片方だけ動かす ))) OUT 0 OUTポート全部を 0 にする。 OUT 1,1 左モータが回転する。(OUT2 がまだ0のため) OUT 1,0 左モータが止まる。 OUT 2,1 左モータが逆転で動く。 OUT 2,0 左モータが止まる。 OUT 5,1 右モータが回転する。 OUT 5,0 OUT 6,1 右モータが逆転で動く。 OUT 6,0 ((( モータを複数動かす ))) OUT 1,1 OUT 5,1 両方のモータが動く OUT 1,0 OUT 5,0 ((( モータを複数同時に設定 ))) OUT '000001 OUT1=1,OUT2=0,OUT5=0,OUT6=0 左モータが動く OUT '010000 OUT1=0,OUT2=0,OUT5=0,OUT6=0 右モータが動く
サーボモータを動かす(PWM2)
MapleSylup では、OUT2,OUT3,OUT4,OUT5 をサーボモータに接続できるが、OUT2 は、直流モータを動かすために使うので、今回は OUT3,OUT4 を使う。
サーボモータを動かすためには、専用の PWM 命令を使う
PWM 3, 100 PWM 3, 200
PWM2, 50 PWM2,100
サーボモータを左右に動かすプログラムは以下のようになる。(練習問題)
10 PWM 3,100 : WAIT 30 20 PWM 3,200 : WAIT 30 30 GOTO 10