项目中的多个开发人员
许多TouchGFX项目涉及多个开发人员。 两个或多个开发人员访问和修改相同的资源可能成为一个问题,但如果您遵循一些最佳实践,则可以减少并解决此类问题。
在TouchGFX 4.18版之前,Excel文件用于保存所有文本资源(翻译)。 在有多个开发人员的项目中,这就成了一个问题,因为如果两个用户都添加或变更了文本,Excel文件将无法合并。 文本必须在各种Excel表格之间手动复制。
从TouchGFX 4.18版开始,使用XML文档保存文本。 大多数情况下,可以使用标准文本工具合并此XML文件。
TouchGFX 设计器保存UI设计的.touchgfx文件中也可能出现冲突。 此文件为JSON格式,也可以使用标准工具合并。
本节中,我们将看到TouchGFX文件中的一些冲突示例及解决办法。
冲突的其他来源是IAR或Keil项目文件,我们将不在这里讨论。
冲突
当两个开发人员修改或添加同一资源时,可能会发生冲突。 在合并两个变更时,大多数工具都无法确定应保留哪些修改(如要删除某些修改)或两者的正确顺序(如两者都保留)。
一般情况如下图所示:
在本例中,从主干创建了两个分支。 Feature2分支首先合并到主干中。 如果也在主干上完成工作,可能会导致合并冲突。 然后,Feature1分支合并到主干中。 如果两个功能分支对相同的文件进行了变更,可能会产生冲突。
TouchGFX项目中有四个主要冲突点:
- TouchGFX UI文件(.touchGFX文件)
- TouchGFX文本数据库(texts.xml文件)
- UI和其它模块中的普通C++代码
- IDE的项目文件
下一节中,我们将了解如何处理和减少TouchGFX文件中的冲突问题。
TouchGFX文件
.touchgfx文件是TouchGFX 设计器保存在Designer中创建的UI定义的位置。 这意味着,如果项目中的两个开发人员都使用TouchGFX 设计器,他们都将对此文件进行变更,并可能发生冲突。
.touchgfx文件基本上由两个数组构成。 第一个数组包含Screen列表。 第二个数组是Container列表。
这是一个使用TouchGFX 设计器创建的非常简单的项目。 由一个Screen和一个名为“box1”的Box控件组成:
.touchgfx文件的屏幕部分如下:
"Screens": [
{
"Name": "Screen1",
"Components": [
{
"Type": "Box",
"Name": "box1",
"Width": 800,
"Height": 480,
"Color": {
"Red": 51,
"Green": 255
}
}
],
"Interactions": []
}
],
屏幕保存在“Screens(屏幕)”下,本例中,仅包含一个名为Screen1的Screen(屏幕)定义。 添加到Screen1的控件位于该Screen(屏幕)内的“Components(组件)”下。 到目前为止,我们只添加了一个名为Box1的Box类Widget。 在Box1上设置了各种属性,例如“Width(宽度)”、“Height(高度)”和“Color(颜色)”。 请注意,默认值不会写入.touchgfx文件,因此box1的Color属性的“Blue”组件(为零)未明确说明。
添加屏幕时发生冲突
我们现在将详细了解为什么会出现冲突,何时添加更多屏幕,以及如何解决冲突。
我们将上述项目作为开发的起点。 创建几个功能分支:
git checkout -b feature1
git checkout -b feature2
假设开发员1检查了分支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": []
}
],
假设开发员2检查了feature2并添加了一个新的Screen(Screen3)。 他的.touchgfx文件看似与上面的文件非常相像。
团队接下来要做的是合并两个功能分支(假设他们完成了任务)。 首先合并feature1:
合并操作进展顺利。 生成的.touchgfx文件与feature1中的文件相同。 但当我们尝试在feature2中合并时,就没那么幸运了:
feature2分支和主干变更了.touchgfx文件,git无法合并这些变更。 这是因为两个分支都在Screen1之后添加了一个Screen:
这次合并有两个问题。 最糟糕的问题是git试图将两个屏幕定义(Screen2和Screen3)合并为一个。 我们需要两个全屏幕定义。 一个接一个。 第二个问题是git(以及任何其它版本控制系统)无法得知要在主干中首先使用哪个Screen,即Screen2还是Screen3。 这取决于开发人员。
因此,我们需要手动合并.touchgfx文件中的变更。 确保在Screen2之后插入Screen3的整个定义(从“{”到“}”)。
您可以使用此命令将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保存或生成代码(F4),将覆盖磁盘上的.touchgfx文件(并删除冲突)!
合并后,您可以在TouchGFX 设计器中打开.touchgfx项目并生成代码。
添加变更属性时发生冲突
当两个开发人员变更控件的相同属性时,也可能会发生冲突。 假设我们创建了两个新的功能分支feature3和feature4,并在这两个分支中将屏幕1中方框的颜色分别更改为黄色和蓝色。
将feature3合并到主干同样没有问题,但在合并其它功能分支时,就会遇到冲突,因为.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 Text Database(文本数据库)(texts.xml文件)会出现与上面示例非常类似的冲突。 看一个例子。 我们首先创建两个功能分支:
git checkout -b feature5
git checkout -b feature6
我们使用feature5向Screen2添加TextArea控件和文本,使用feature6向Screen3添加TextArea。 该示例模拟了两个开发人员在不同Screen上工作。
现在假设开发员1检查了feature5并添加了一个TextArea。
TextArea由Designer、Screen2Headline和文本内容“Screen 2”赋予一个资源ID (1)。 当开发人员在TouchGFX 设计器中保存项目时,会在磁盘上修改两个文件:
开发人员添加两个文件并提交。 texts.xml的差异(见下文)显示,我们添加了一个ID为Screen2Headline的TextXML元素和一个带英语文本的Translation元素。 文本元素位于新的TextGroup元素内。 此元素由设计者创建,因为我们没有将新文本放在特定的组中:
另一位开发人员执行了类似的变更,尽管他的文本名为Screen3Headline,文本是“My Headline”:
同样,完成功能修改后,必须合并到主干。 对于第一个分支feature5来说,很轻松,但在合并feature6时,我们再次遇到了冲突。 这次在tests.xml中:
自分支创建以来,.touchgfx文件在主干和feature6上进行了变更,但git能够合并变更集,因为这些变更位于文件中的不同位置(不在同一Screen中)。 冲突的产生是因为两者都变更了“Unsorted(未排序)”TextGroup元素中的第一个(或最后一个)文本:
对于这种冲突,文本的顺序并不重要,但必须生成正确的XML,因此我们手动合并文本文件,获得两个完整的Text(文本)元素:
避免冲突
我们上面看到的一些冲突可以通过遵循一些简单的规则来避免。
始终在主干上创建新Screen。 当在主干上创建Screen时,两个功能分支不可能在.touchgfx文件的同一位置插入新Screen。 不要将Screen称为Screen1、Screen2等。 许多小组经常使用的一种方法是在项目早期创建所有Screen(大多数情况下为空)。 如果在项目开始时为大多数应用程序提供了图形设计,这是可行的。 如果以后需要添加屏幕,请考虑将其添加到具有逻辑连接的Screen旁边,而不必作为最后一个Screen。 通过Designer 中添加Screen,然后将新屏幕向上拖动到其最终位置来完成。
Custom Container(自定义容器)显示与Screen相同的冲突。 由于在Screen开发过程中需要自定义容器,因此提前在主干上创建容器可能不太实际。 考虑在主干上创建自定义容器,将功能分支重新设置为主干,然后继续开发。 另一种方法是完成功能分支并将其重设为主干。 这里会出现冲突,可以通过获取功能分支中最后一次提交时的整个容器块并手动将其放入.touchgfx文件中,同时使主干的容器保持不变来解决。 现在合并到主干。 主干不会出现任何冲突。
通过使用多个TextGroup(文本组)而不是将所有文本放在“Unsorted(未排序)”组中,来减少文本冲突。 在这里,我们为Screen3创建了一个TextGroup,将旧文本移动到此组,并添加了一个新文本(SecondHeadline):
版本控制系统
当多个开发人员在一个项目上工作时,使用版本控制系统很有益处。 比如:git,如上文所述。 这就产生了一个问题,TouchGFX项目中,版本控制系统保存哪些文件?
Tip
原因是为了避免在诸如.touchgfx文件及其生成的文件变更时发生混淆。 如果他们不同意,您保留哪个修改。 如果在.touchgfx文件中出现冲突,则在生成的文件中也会出现冲突。 这意味着需要更多的工作来解决所有冲突。
文件夹 | 内容 | 保存在VCS中 |
---|---|---|
Core | main.c和其它文件进行初始化。 这些由CubeMX重新生成,但可将代码添加到标记的“User Sections(用户部分)” | 是 |
Drivers | STM32 HAL和BSP软件 | 是 |
EWARM | IAR EWARM项目文件。 编译器将针对编译文件和其它输出文件创建子目录。 这些不应保存。 | (是) |
gcc | ARM gcc生成文件和链接器脚本 | 是 |
LIBJPEG | 如启用LibJPEG中间件,则由CubeMX生成 | 是 |
MDK-ARM | Keil项目文件。 不保存任何子目录 | (是) |
Middlewares | 任何已启用中间件的源代码或头文件和库。 含TouchGFX | 是 |
STM32CubeIDE | CubeIDE项目文件 | 是 |
TouchGFX | 见下文 |
文件夹 | 内容 | 保存在VCS中 |
---|---|---|
App | CubeMX生成。 从main.c启动TouchGFX的功能 | 是 |
assets | TouchGFX的字体、图像和文本 | 是 |
build | ARM和windows gcc的编译器输出 | 否 |
config | gcc和MSVS的配置文件 | 是 |
generated | TouchGFX工具和Designer生成的文件。 user.config文件包含TouchGFX安装路径。 如果添加这些文件,通常会导致冲突。 | 否 |
gui | Screen用户代码。 如缺失,则由TouchGFX Designer生成。 | 是 |
simulator | 模拟器特定代码 | 是 |
target | CubeMX生成的目标特定代码 | 是 |
application.config | 图像和文本格式的配置。 由TouchGFX 设计器和其它工具使用。 | 是 |
ApplicationTemplate.touchgfx.part | 项目的各种相关信息。 由CubeMX生成,由TouchGFX Designer读取。 | 是 |
F746TextureMapper.touchgfx | 使用TouchGFX 设计器创建的UI定义。 | 是 |
标准建议是将CubeMX生成的所有文件保存在版本控制系统中。 相反,不应保存TouchGFX 设计器生成的文件。 其原因是,在许多项目中,CubeMX主要用于创建硬件配置。 因此,最好不要在每次从版本控制系统更新后打开CubeMX并生成代码。 TouchGFX 设计器中进行了更多的变更,所以许多开发人员无论如何都需要打开该应用程序。 因此,TouchGFX 设计器中可轻松地重新生成代码。
此建议的有效性取决于您的项目组。 如果您有一些不使用UI的开发人员,最好也提交TouchGFX/generated文件夹,这样他们就可以直接在VCS中获得更完整的项目。
从命令行生成TouchGFX代码
可从命令行重新生成TouchGFX代码。 典型用例是构建服务器或任何其它CI设置。
位于TouchGFX安装包中的tgfx.exe工具提供了命令行界面。 要重新生成上述项目的代码,请运行以下命令:
d:/TouchGFX/4.20.0/designer/tgfx.exe generate -p TouchGFX/F746TextureMapper.touchgfx
运行此命令与TouchGFX 设计器中生成代码(F4)相对应。 如果在构建服务器上使用此命令,可避免签入生成的文件。
忽略特定文件
许多版本控制系统可配置为忽略特定的文件名和文件夹。
以下是TouchGFX项目的典型.gitignore:
TouchGFX/build/
TouchGFX/generated/
EWARM/settings/
EWARM/STM32F746/
此忽略列表使git忽略生成的TouchGFX文件和任何编译文件。 我们还忽略任何用户设置文件并为IAR生成输出。