複数の開発者が関わるプロジェクト
多くのTouchGFXプロジェクトには複数の開発者が関わります。 複数の開発者が同じリソースにアクセスし変更することで問題が生じる可能性がありますが、いくつかのベスト・プラクティスに従えば、こうした問題を軽減させ、うまく処理することができます。
TouchGFXバージョン4.18より前は、すべてのテキスト・リソース(翻訳)を保持するためにExcelファイルを使用していました。 これは複数の開発者が関わるプロジェクトで問題になっていました。2人のユーザがテキストを追加または変更した場合、Excelファイルではその変更をマージできないからです。 さまざまなExcelシート間で、テキストを手動でコピーする必要がありました。
TouchGFXバージョン4.18以降、XMLドキュメントを使用してテキストを保持するようになりました。 このXMLファイルは、ほとんどの場合、標準のテキスト・ツールを使用してマージできます。
TouchGFX DesignerのUI設計が保存される.touchgfxファイルでも、競合が発生する可能性があります。 このファイルはJSONフォーマットなので、こちらも標準ツールを使用してマージできます。
このセクションでは、TouchGFXファイルで生じる競合例をいくつか示し、それらの解決方法について説明します。
IARまたはKeilプロジェクト・ファイルでも競合が発生することがありますが、ここでは取り上げません。
競合
2人の開発者が同じリソースに対して変更または追加を行うと、競合が発生する可能性があります。 2つの変更をマージする場合、ほとんどのツールでは、保持すべき変更(いくつかを削除する場合)または2つの変更の正しい順序(両方保持する場合)を判断できません。
下の図は一般的な状況を示しています。
この例では、メイン・ブランチから2つのブランチが作成されています。 Feature2ブランチは、最初にメイン・ブランチにマージされます。 メイン・ブランチでも作業が行われた場合、マージ競合が発生する可能性があります。 その後、Feature1ブランチがメイン・ブランチにマージされます。 2つのFeatureブランチで同じファイルに変更が行われた場合、競合が発生する可能性があります。
TouchGFXプロジェクトでは、次の4つの主要ポイントで競合が起こりえます。
- TouchGFX UIファイル(.touchgfxファイル)
- TouchGFXテキスト・データベース(texts.xmlファイル)
- UIやその他のモジュールにおける通常のC++コード
- IDE向けのプロジェクト・ファイル
以下のセクションでは、TouchGFXファイルで競合による問題を処理および削減する方法について説明します。
TouchGFXファイル
.touchgfxファイルには、TouchGFX Designerで作成されたUIの定義が保存されます。 つまり、プロジェクト内の2人の開発者がTouchGFX Designerで作業を行う場合、どちらもこのファイルを変更できるため、競合が発生する可能性があります。
.touchgfxファイルは基本的に2つの配列で構成されます。 1つ目の配列にはScreenのリストが含まれます。 2つ目の配列はContainerのリストです。
次に、TouchGFX Designerを使用して作成した、非常にシンプルなプロジェクトを示します。 これは、"box1"という名前の1つのBoxウィジェットを含む1つのScreenで構成されています。
.touchgfxファイルのScreensセクションは次のようになります。
"Screens": [
{
"Name": "Screen1",
"Components": [
{
"Type": "Box",
"Name": "box1",
"Width": 800,
"Height": 480,
"Color": {
"Red": 51,
"Green": 255
}
}
],
"Interactions": []
}
],
Screenは"Screens"の下に保存されます。この例では、Screen1という名前の1つのScreen定義のみ含まれています。 Screen1に追加されたウィジェットは、Screen内にある"Components"の下に表示されています。 この時点では、タイプがBoxで名前がbox1である1つのウィジェットが追加されているだけです。 box1にはさまざまな属性が設定されています(Width、Height、Colorなど)。 .touchgfxファイルにはデフォルト値が書き込まれないので、box1のColor属性の"Blue"コンポーネントがゼロであることは明示的に記述されません。
Screen追加時の競合
次に、Screenをさらに追加すると競合が発生する理由と、その競合の解決方法について見ていきます。
上記のプロジェクトを開発の開始点とします。 次に、2つのfeatureブランチを作成しましょう。
git checkout -b feature1
git checkout -b feature2
developer1がfeature1ブランチをチェックアウトして、Screen2を追加するとします。 .touchgfxファイルには、Screen1の後にScreen2のScreen定義も含まれることになります。
"Screens": [
{
"Name": "Screen1",
...
},
{
"Name": "Screen2",
"Components": [
{
"Type": "Box",
"Name": "box1",
"Width": 800,
"Height": 480,
"Color": {
"Red": 255,
"Green": 255,
"Blue": 255
}
}
],
"Interactions": []
}
],
次に、developer2がfeature2をチェックアウトして、こちらも新しいScreen(Screen3)を追加するとします。 このときのdeveloper2の.touchgfxファイルは、上記のファイルと非常によく似たものになります。
このチームが次に行うことは、2つのfeatureブランチのマージです(それぞれのタスクは完了しているとします)。 まずは、feature1をマージしましょう。
このマージ操作はうまくいきました。 作成された.touchgfxファイルはfeature1のものと同じです。 しかし、feature2でマージを行おうとすると、今度はうまくいきません。
feature2ブランチとメイン・ブランチの両方で.touchgfxファイルが変更され、Gitは変更をマージできませんでした。 どちらのブランチもScreen1の直後にスクリーンを追加したためです。
このマージには2つの問題があります。 最大の問題は、Gitが2つのスクリーン定義(Screen2とScreen3)を1つにマージしようとしていることです。 ここで求めているのは、2つの完全なスクリーン定義が 1つずつ順番に存在する状態です。 2つ目の問題は、Git(およびその他のバージョン管理システム)が、メイン・ブランチに最初に配置したいScreenがScreen2またはScreen3であるかを判断できないことです。 これは開発者が処理する必要があります。
このため、.touchgfxファイル内で変更を手動でマージする必要があります。 必ず、Screen3の定義全体('{'から'}'まで)をScreen2の後に挿入してください。
次のコマンドを使用すると、Multiple.touchgfxファイルをマージ開始前の状態に戻すことができます。
git checkout --ours Multiple.touchgfx
feature2から.touchgfxファイルを取得するには、次のコマンドを使用します。
git show feature2:Multiple1.touchgfx > feature2.touchgfx
次に、お気に入りのエディタでfeature2.touchgfxとMultiple1.touchgfxを開き、Screen3定義全体をfeature2.touchgfxからMultiple1.touchgfxにコピーします。 次のような状態になります(スクリーン間のコンマを忘れないでください)。
"Screens": [
{
"Name": "Screen1",
...
},
{
"Name": "Screen2",
...
},
{
"Name": "Screen3",
...
}
],
この競合の修正後、マージをコミットできます。
git commit -m 'Merged feature2'
マージ中にはTouchGFX Designerでプロジェクトを開かないでください。 Designerでコードの保存または生成(F4)を行うと、ディスク上の.touchgfxファイルが上書きされます(これで競合がなくなります)。
マージ後は、TouchGFX Designerで.touchgfxプロジェクトを開き、コードを生成することができます。
属性変更の追加時の競合
2人の開発者がウィジェットの同じプロパティを変更した場合にも競合が発生します。 2つの新しいfeatureブランチ(feature3とfeature4)を作成し、どちらのブランチでもScreen1にあるボックスの色を、それぞれ黄色と青色に変更する場合を考えてみてください。
feature3のメインへのマージでは、ここでも問題は発生しません。しかし、もう一方のfeatureブランチのマージでは競合が発生します。.touchgfxファイル内の同じ行が変更されるからです。
ここでの解決方法は、feature3とfeature4のどちらのプロパティ(色)を使用するのか、開発者に確認して決定することです。 当然ながら、両方使用することはできません。 おそらく混ぜるべきでもありません(白色になります)。 エディタまたはお気に入りのGitマージ・ツールで.touchgfxファイルを開き、目的の行を選択します。 ここではfeature3のプロパティを選択するので、次のようになります。
"Name": "Screen1",
"Components": [
{
"Type": "Box",
"Name": "box1",
"Width": 800,
"Height": 480,
"Color": {
"Red": 255,
"Green": 255
}
}
...
Note
TouchGFXテキスト・データベース・ファイル
TouchGFXテキスト・データベース(texts.xmlファイル)では、これまでに見てきたものと非常によく似た競合が発生する可能性があります。 例を見てみましょう。 最初に2つのfeatureブランチを作成します。
git checkout -b feature5
git checkout -b feature6
feature5を使用してTextAreaウィジェットとテキストをScreen2に追加し、feature6を使用してTextAreaをScreen3に追加します。 これによって、別々のScreenに対して作業する2人の開発者をシミュレートします。
次に、developer1がfeature5をチェックアウトして、TextAreaを追加するとします。
TextAreaにはDesignerによってScreen2HeadlineというリソースID(1)が付与されており、テキストのコンテンツは"Screen 2"です。 開発者がプロジェクトをTouchGFX Designerに保存すると、ディスク上の2つのファイルが変更されます。
開発者は両方のファイルを追加し、コミットします。 texts.xmlのdiff(下記参照)には、IDがScreen2HeadlineであるText XML要素と英語テキストのTranslation要素が追加されたことが示されています。 Text要素は新しいTextGroup要素の中にあります。 この要素は、特定のグループに新しいテキストが追加されなかったため、Designerが自動的に作成したものです。
別の開発者も同様の変更を行いますが、そちらのテキストのIDはScreen3Headlineで、テキストは"My Headline"です。
ここでも、featureの処理が完了したらメインにマージする必要があります。 最初のブランチ(feature5)は問題ありませんが、feature6をマージするとやはり競合が発生します。 今回の競合は、tests.xml内で発生します。
ブランチが作成されてから、メインとfeature6で.touchgfxファイルも変更されましたが、これらの変更はファイル内の別々の場所に存在する(同じScreen内にない)ため、Gitは変更セットをマージすることができました。 両方で"Unsorted"というTextGroup要素内の最初(または最後)のテキストが変更されているため、競合が発生します。
この競合ではテキストの順序は重要ではありませんが、もちろん正しいXMLを作成することは必須です。そこで、テキスト・ファイルを手動でマージし、2つの完全なText要素を作成します。
競合の回避
ここまで見てきた競合の一部は、以下の単純なルールに従うことで回避できます。
新しいScreenは常にメイン・ブランチで作成します。 Screenがメイン・ブランチに作成されていれば、2つのfeatureブランチが.touchgfxファイルの同じ場所に新しいScreenを挿入するのは不可能になります。 Screen1、Screen2など、複数のScreenを呼び出さないでください。 多くのグループでよく使用されている方法は、プロジェクトの初期段階ですべてのScreen(ほとんどの場合は空)を作成してしまうことです。 プロジェクトの開始時点でほとんどのアプリケーションのグラフィック設計が入手できていれば、これは可能です。 スクリーンを後から追加する必要が生じた場合は、論理接続によって別のScreenの隣に追加することを検討してみてください。最後のScreenにする必要はありません。 これを行うには、DesignerでScreenを追加し、次にその新しいScreenを目的位置まで上にドラッグします。
カスタム・コンテナでもスクリーンと同じ競合が発生します。 カスタム・コンテナはScreenの開発途中で必要になるため、前もってメイン・ブランチでコンテナを作成することは現実的でないでしょう。 メイン・ブランチにカスタム・コンテナを作成し、featureブランチをメインにリベースしてから開発を続行する方法を検討してみてください。 もう1つの方法は、featureブランチを終了してからメインにリベースすることです。 ここで競合が発生する可能性はありますが、featureブランチ内の最終コミット時点のコンテナ・ブロック全体を取得し、それを.touchgfxファイル内に手動で配置することで解決できます。このときメインのコンテナは何も変更せずそのままです。 次に、メインにマージします。 メイン・ブランチでは競合は発生しません。
テキスト競合は、すべてのテキストを"Unsorted"グループ内に配置するのではなく、複数のTextGroupを使用することで削減できます。 ここではScreen3用のTextGroupを作成し、古いテキストをこのグループに移動して、新しいテキスト(SecondHeadline)を追加しています。
バージョン管理システム
複数の開発者が1つのプロジェクトで作業する場合、バージョン管理システムを使用することが有益です。 たとえば、上記で使用してきたGitもその1つです。 ここで、TouchGFXプロジェクトでバージョン管理システムに保存するのはどのファイルなのかという問題が持ち上がります。
Tip
この理由は、たとえば.touchgfxファイルとそこから生成されたファイルが変更された場合の混乱を避けるためです。 このルールが守られない場合、どの変更を保持するのか区別がつきません。 .touchgfxファイルに競合が発生した場合は、生成されたファイルにも競合が発生します。 つまり、競合を解決するための作業量が増えるのです。
フォルダ | 内容 | VCSへの保存 |
---|---|---|
Core | 初期化用のmain.cなどのファイル。 これらのファイルはCubeMXで再生成されるが、"User Sections"とマークされた部分にコードを追加する方がよい。 | 可能 |
Drivers | STM32 HALおよびBSPソフトウェア | 可能 |
EWARM | IAR EWARMプロジェクトのファイル。 コンパイラがコンパイル済みファイルとその他の出力ファイル用にサブディレクトリを作成する。 これらのファイルは保存しないこと。 | (Yes) |
gcc | ARM gcc makefileおよびリンカ・スクリプト | 可能 |
LIBJPEG | LibJPEGミドルウェアが有効な場合、CubeMXによって生成される。 | 可能 |
MDK-ARM | Keilプロジェクト・ファイル。 どのサブディレクトリも保存しないこと。 | (Yes) |
Middlewares | 有効なミドルウェアのソース・コードまたはヘッダ・ファイルとライブラリ。 TouchGFXを含む。 | 可能 |
STM32CubeIDE | CubeIDEプロジェクト・ファイル。 | 可能 |
TouchGFX | 下記を参照。 |
フォルダ | 内容 | VCSへの保存 |
---|---|---|
App | CubeMXによって生成される。 main.cからTouchGFXを開始するための関数。 | 可能 |
assets | TouchGFXのフォント、画像、テキスト | 可能 |
build | ARMおよびWindows gccのコンパイラ出力 | No |
config | gccとMSVSの設定ファイル | 可能 |
generated | TouchGFXツールとDesignerによって生成されたファイル。 user.configファイルにはTouchGFXインストールのパスが格納される。 これらのファイルを追加すると、競合が発生することが多い。 | No |
gui | Screenのユーザ・コード。 見つからない場合はTouchGFX Designerによって生成される。 | 可能 |
simulator | シミュレータ固有のコード | 可能 |
target | CubeMXによって生成されるターゲット固有のコード | 可能 |
application.config | 画像およびテキスト・フォーマットの設定。 TouchGFX Designerやその他のツールで使用される。 | 可能 |
ApplicationTemplate.touchgfx.part | プロジェクトに関するさまざまな情報。 CubeMXで生成され、TouchGFX Designerによって読み出される。 | 可能 |
F746TextureMapper.touchgfx | TouchGFX Designerで作成されるUI定義 | 可能 |
基本的なアドバイスとしては、CubeMXによって生成されたすべてのファイルをバージョン管理システムに保存することです。 反対に、TouchGFX Designerによって生成されたファイルは保存してはなりません。 その理由は、多くのプロジェクトでは主にCubeMXを最初に使用して、ハードウェア設定を作成するからです。 つまり、バージョン管理システムからの更新のたびに、CubeMXを開いてコードを生成する必要はないわけです。 TouchGFX Designerではもっと多くの変更が行われるので、多くの開発者はともかくこのアプリケーションを開いておく必要があります。 このため、TouchGFX Designerでのコードの再生成に負荷は生じません。
ここでのアドバイスが有効かどうかはプロジェクト・グループによって異なります。 UIを操作しない開発者がグループに存在する場合は、TouchGFX/generatedフォルダもコミットして、そういう開発者がVCSでプロジェクト全体を直接的に管理できるようにした方がよいかもしれません。
コマンドラインからのTouchGFXコードの生成
TouchGFXコードはコマンドラインで再生成できます。 代表的な使用例として、ビルド・サーバやその他のCI設定があります。
コマンドラインのインタフェースは、TouchGFXインストール内にあるtgfx.exeツールによって提供されます。 上記のプロジェクトのコードを再生成するには、次のコマンドを実行します。
d:/TouchGFX/4.20.0/designer/tgfx.exe generate -p TouchGFX/F746TextureMapper.touchgfx
このコマンドの実行は、TouchGFX Designer内のコード生成(F4)に対応します。 このコマンドをビルド・サーバで使用すると、生成済みファイル内のチェックを回避できます。
特定のファイルの無視
多くのバージョン管理システムでは、特定のファイル名やフォルダを無視するように設定できます。
TouchGFXプロジェクトでの代表的な.gitignoreを次に示します。
TouchGFX/build/
TouchGFX/generated/
EWARM/settings/
EWARM/STM32F746/
この無視リストによって、Gitは生成されたTouchGFXファイルとコンパイル済みのファイルを無視することになります。 さらに、IARのユーザ設定ファイルとビルド出力も無視します。