gitlab CI/CD 实现自动编译 Keil 和 IAR 工程

前置知识

  1. 什么是 gitlab?
  2. 什么是 CI/CD?
  3. Windows 下如何通过命令行编译 KeilIAR 工程?
  4. 如何安装 gitlab-runner?

思路

我尝试过的方案有 3 个:

  1. 将 gitlab-runner 安装在本地 Win10 里,executor 选择的是 shell,本地安装了 Keil 和 IAR
  2. 在一台 Windows10 电脑安装 docker(Windows 后端),使用 mcr.microsoft.com/windows:20H2 镜像,在该 docker 实例中安装 keil 和 IAR
  3. gitlab-runner 安装在 Ubuntu 服务器里,executor 选择 virtualbox,在 Ubuntu 服务器安装 virtualbox,在 virtualbox 内安装 Win10,在 Win10 安装 Keil 和 IAR

各方案优劣

gitlab-runner(Win10) + executor shell

此方案是可行的,不过每次编译,gitlab-runner 都会使用自己生成的 PATH,必须在 .gitlab-ci.yml 里写 Keil 和 IAR 的绝对路径,否则无法找到 Keil 和 IAR。另外,本地 PC shell 作为 gitlab-runner 的风险极高,若 .gitlab-ci.yml 中写了删除某些文件的命令,当路径写错时,很可能会把本地 PC 的重要文件删除。此方案通用性不好,危险系数还很高,不建议使用。

gitlab-runner(Win10) + executor docker + mcr.microsoft.com/windows:20H2 镜像

经过一番搜索,官方明确说明,这个镜像不支持 GUI 操作,也就无法使用远程桌面(RDP)或者 VNC 登陆,这就无法安装 Keil 和 IAR 了,退一步讲,即使让我成功安装进去了,Keil 编译时会启动 GUI(可以设置在编译时隐藏 GUI),但经过我的测试,没有 GUI 是无法启动 Keil 编译的,如何测试:Win10 + WSL2,直接打开 WSL 调用 Keil 可正常编译,通过 ssh 连接 WSL(没有 GUI),Keil 编译会报错,根本无法启动编译,遂放弃此方案。
以下两个链接是关于 docker Windows 镜像不支持 GUI 的说明:
链接1
链接2

gitlab-runner(Ubuntu) + executor virtualbox + Win10(21H2)

参考上面 链接2 中的说明:

Greetings,
Please use VMs instead. Your test workflow doesn’t need to change.
Containers are suitable for deploy web and console applications.
The thread that referenced by you is related to Ubuntu.

再结合 gitlab 官方文档(virtualbox)

VirtualBox allows you to use VirtualBox’s virtualization to provide a clean build environment for every build. This executor supports all systems that can be run on VirtualBox. The only requirement is that the virtual machine exposes an SSH server and provides a shell compatible with Bash or PowerShell.

根据 gitlab 官方的说法,这个方案可以支持任意操作系统(操作系统要能在 VirtualBox 中运行),每次启动编译,都会创建一个虚拟机快照(若原始虚拟机有更新才创建新快照,无更新则使用旧快照),并在快照的基础上启动虚拟机,进行编译。这个方案可解决方案 1 可能删除 gitlab-runner 宿主系统文件的风险,在每次执行完毕后可做到无残留,正是我选择的方案。以下是这个方案的架构:

B 服务器 - ubuntu
A 服务器 - ubuntu
本地 PC
virtualbox
Windows10
msys64
push
CI/CD
ssh
gitlab-runner
sshd
pwsh
git
gitlab-runner
Keil
IAR
make
gitlab 网站
git

具体如何搭建环境可参考 gitlab 官方文档(virtualbox),在 Checklist for Windows VMs 中,我使用的是 Use native OpenSSH and PowerShell 方案

方案 3 踩坑

坑 1:

gitlab-runner ssh 连接 virtualbox 里的 Win10 报错ssh Dial() error: ssh: handshake failed: knownhosts: key is unknown

Running with gitlab-runner 15.0.0 (febb2a09)
  on virtual_box_win10 WXfibXkD
Preparing the "virtualbox" executor
10:52
Using VirtualBox version 6.1.34r150636 executor...
Creating new VM...
ERROR: Preparation failed: ssh Dial() error: ssh: handshake failed: knownhosts: key is unknown
Will be retried in 3s ...
Using VirtualBox version 6.1.34r150636 executor...
Creating new VM...
ERROR: Preparation failed: ssh Dial() error: ssh: handshake failed: knownhosts: key is unknown
Will be retried in 3s ...
Using VirtualBox version 6.1.34r150636 executor...
Creating new VM...
ERROR: Preparation failed: ssh Dial() error: ssh: handshake failed: knownhosts: key is unknown
Will be retried in 3s ...
ERROR: Job failed (system failure): ssh Dial() error: ssh: handshake failed: knownhosts: key is unknown

查看 gitlab-runner 源码,在 这里 看到了DisableStrictHostKeyChecking的说明:

package ssh

//nolint:lll
type Config struct {
	User                         string `toml:"user,omitempty" json:"user" long:"user" env:"SSH_USER" description:"User name"`
	Password                     string `toml:"password,omitempty" json:"password" long:"password" env:"SSH_PASSWORD" description:"User password"`
	Host                         string `toml:"host,omitempty" json:"host" long:"host" env:"SSH_HOST" description:"Remote host"`
	Port                         string `toml:"port,omitempty" json:"port" long:"port" env:"SSH_PORT" description:"Remote host port"`
	IdentityFile                 string `toml:"identity_file,omitempty" json:"identity_file" long:"identity-file" env:"SSH_IDENTITY_FILE" description:"Identity file to be used"`
	DisableStrictHostKeyChecking *bool  `toml:"disable_strict_host_key_checking,omitempty" json:"disable_strict_host_key_checking" long:"disable-strict-host-key-checking" env:"DISABLE_STRICT_HOST_KEY_CHECKING" description:"Disable SSH strict host key checking"`
	KnownHostsFile               string `toml:"known_hosts_file,omitempty" json:"known_hosts_file" long:"known-hosts-file" env:"KNOWN_HOSTS_FILE" description:"Location of known_hosts file. Defaults to ~/.ssh/known_hosts"`
}

func (c *Config) ShouldDisableStrictHostKeyChecking() bool {
	return c.DisableStrictHostKeyChecking != nil && *c.DisableStrictHostKeyChecking
}

根据说明和类型,在 config.toml 的 [runners.ssh]字段之下添加这句话:

disable_strict_host_key_checking = true

重启 gitlab-runner 即可:

sudo gitlab-runner restart

坑 2:只支持 pwsh,不支持 powershell

在最后一步:

Add shell pwsh to config.toml

我遇到了一个坑,因为之前搜索说 pwsh 就是 powershell,所以我在 config.toml 中直接将 powershell 作为 shell,实际运行时报错virtualbox doesn't support shells that require script file

Running with gitlab-runner 15.0.0 (febb2a09)
  on virtual_box_win10 WXfibXkD
Preparing the "virtualbox" executor
00:09
ERROR: Preparation failed: virtualbox doesn't support shells that require script file
Will be retried in 3s ...
ERROR: Preparation failed: virtualbox doesn't support shells that require script file
Will be retried in 3s ...
ERROR: Preparation failed: virtualbox doesn't support shells that require script file
Will be retried in 3s ...
ERROR: Job failed (system failure): virtualbox doesn't support shells that require script file

分析 gitlab-runner 源码 发现是 这里 打印的:

func (s *executor) validateConfig() error {
	if s.Config.VirtualBox.BaseName == "" {
		return errors.New("missing BaseName setting from VirtualBox configuration")
	}

	if s.BuildShell.PassFile {
		return errors.New("virtualbox doesn't support shells that require script file")
	}

	if s.Config.SSH == nil {
		return errors.New("missing SSH config")
	}

	if s.Config.VirtualBox == nil {
		return errors.New("missing VirtualBox configuration")
	}

	return s.ValidateAllowedImages(s.Config.VirtualBox.AllowedImages)
}

搜索关键字 PassFile,在 这里 找到了它的赋值语句:

......
const (
	dockerWindowsExecutor = "docker-windows"

	SNPwsh       = "pwsh"
	SNPowershell = "powershell"

	// Before executing a script, powershell parses it.
	// A `ParserError` can then be thrown if a parsing error is found.
	// Those errors are not catched by the powershell_trap_script thus causing the job to hang
	// To avoid this problem, the PwshValidationScript is used to validate the given script and eventually to cause
	// the job to fail if a `ParserError` is thrown
	pwshJSONTerminationScript = `
param (
	[Parameter(Mandatory=$true,Position=1)]
	[string]$Path
)
......
func (b *PowerShell) GetConfiguration(info common.ShellScriptInfo) (*common.ShellConfiguration, error) {
	script := &common.ShellConfiguration{
		Command:       b.Shell,
		PassFile:      b.Shell != SNPwsh && info.Build.Runner.Executor != dockerWindowsExecutor,
		Extension:     "ps1",
		DockerCommand: PowershellDockerCmd(b.Shell),
	}

	if info.User != "" {
		if script.PassFile {
			return nil, &powershellChangeUserError{
				shell:    b.Shell,
				executor: info.Build.Runner.Executor,
			}
		}

		script.Command = "su"
		if runtime.GOOS == OSLinux {
			script.Arguments = append(script.Arguments, "-s", "/usr/bin/"+b.Shell)
		}
		script.Arguments = append(
			script.Arguments,
			info.User,
			"-c",
			b.Shell+" "+strings.Join(stdinCmdArgs(), " "),
		)
	} else {
		script.Arguments = b.scriptArgs(script)
	}

	script.CmdLine = strings.Join(append([]string{script.Command}, script.Arguments...), " ")

	return script, nil
}

可见,当 Executor不是dockerWindows,且Shell类型不是pwsh时,PassFile会被赋值为true,导致前面的报错。所以,在本方案中,config.toml 只能将 pwsh 作为 shell,归根结底,还是我没有去了解 pwsh 和 powershell 的差别,而引发了这个疏忽,Windows10 自带的 powershell 是 powershell 5 版本,而 pwsh 是 powershell 6 以上版本,pwsh 更易用于开发,在 virtualbox + Win10 的情况下,gitlab 官方只支持 pwsh,而没有支持 powershell 5。
最终 config.toml 如下

concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "virtual_box_win10"
  url = "http://xxx.yyy.zzz/"
  token = "WXfibXkDcdGiYCbNBuoz"
  executor = "virtualbox"
  shell = "pwsh"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.ssh]
    user = "AAAA"
    password = "BBBBCCCC"
    identity_file = "/root/.ssh/virtualbox_id_rsa"
    disable_strict_host_key_checking = true
  [runners.virtualbox]
    base_name = "Windows10_x64"
    base_folder = ""
    disable_snapshots = false

坑 3:Win10 pwsh 使用的是 GB2312 编码,而 gitlab-runner 传给它的却是 UTF-8 编码

Win10 pwsh 使用的是 GB2312 编码,而 gitlab-runner 传给它的却是 UTF-8 编码,中文编码错误,导致 pwsh 语法错误。
具体分析和解决方案,我写了一篇文章,可直接阅读 这篇文章
贴一下最终解决方案(修改executors/virtualbox/virtualbox.go并编译),使用mahonia,在 pwsh 命令发出前,将其自 UTF-8 转码为 GBK,问题即可修复:

package virtualbox

import (
	"errors"
	"fmt"
	"time"

	"gitlab.com/gitlab-org/gitlab-runner/common"
	"gitlab.com/gitlab-org/gitlab-runner/executors"
	"gitlab.com/gitlab-org/gitlab-runner/executors/vm"
	"gitlab.com/gitlab-org/gitlab-runner/helpers/ssh"
	vbox "gitlab.com/gitlab-org/gitlab-runner/helpers/virtualbox"

	"github.com/NuoMinMin/mahonia" // UTF-8 转 GBK
)
......
func (s *executor) Run(cmd common.ExecutorCommand) error {
	// s.Println("s.BuildShell.CmdLine: " + s.BuildShell.CmdLine)
	// s.Println("cmd.Script: " + cmd.Script)
	// 将 UTF-8 转换成 gbk, 因为 pwsh 用的是 gbk 编码, 不这么修改的话, 有些字会被解析成 " 导致执行 pwsh 报错
	cmd.Script, _ = mahonia.NewEncoder("gbk").ConvertStringOK(cmd.Script)
	err := s.sshCommand.Run(cmd.Context, ssh.Command{
		Command: s.BuildShell.CmdLine,
		Stdin:   cmd.Script,
	})
	if exitError, ok := err.(*ssh.ExitError); ok {
		exitCode := exitError.ExitCode()
		err = &common.BuildError{Inner: err, ExitCode: exitCode}
	}
	return err
}
......

这个 bug,gitlab-runner v15.0.0 和 v15.1.0 测试均存在,上述修复方案并非通用修复方案,会影响 virtualbox + Ubuntu 的使用,我已经给 gitlab-runner 官方仓库提交了 Issue,期待官方的修复方案。
一个通用的修复思路:

  1. 在 powershell.go 中检测 Windows 的编码类型
  2. 在 powershell.go 中生成 pwsh 命令后,返回前将其由 UTF-8 编码转为刚刚获得的 Windows 的编码类型

另一个修复思路:

  • 在 virtualbox 安装 Win10 后,将系统编码类型改为 UTF-8,可参考 这篇文章,我没有验证过,理论上可行
STM32CubeMX软件功能介绍: STM32CubeMX是一款STM32微控制器配置器和代码生成器,可以通过图形方式为STM32微控制器生成初始化代码。它可以将所有STM32微控制器的外设和引脚分配进行图形化配置,生成C代码并自动创建IARKeil工程,大大加快了开发速度。 以下是STM32CubeMX软件的主要功能: 1. 选择芯片型号:可以从STMicroelectronics的产品线中选择任何STM32微控制器型号。 2. 配置时钟:可以配置时钟源,时钟输出和时钟分频器等参数。 3. 配置外设:可以配置所有外设,包括GPIO、ADC、DAC、USART、SPI、I2C、CAN、USB等。 4. 配置引脚:可以将外设与芯片上的引脚进行映射,确保正确的引脚配置。 5. 自动代码生成:可以根据用户所选择的配置自动生成C代码,并将其导出到IARKeil工程中。 6. 可视化界面:所有配置都可以通过可视化界面完成,无需手动编写代码。 7. 支持多种操作系统:STM32CubeMX支持Windows、Linux和MacOS等操作系统。 Keil5软件功能介绍: Keil5是一款基于ARM Cortex-M微控制器的嵌入式开发环境,它提供了一整套完整的工具链,包括编译器、调试器、仿真器和IDE等。以下是Keil5软件的主要功能: 1. 代码编辑:支持多种语言和语法高亮显示,包括汇编语言、C语言和C++语言。 2. 编译器:支持多种编译器,包括ARM、Keil C51和Keil C166等。 3. 调试器:支持多种调试器,包括ULINK系列仿真器和J-Link仿真器等。 4. 仿真器:支持多种仿真器,可以模拟各种外设并进行软件仿真。 5. IDE:提供了完整的集成开发环境,包括项目管理、代码编辑、编译、调试和仿真等功能。 6. 支持多种操作系统:Keil5支持Windows、Linux和MacOS等操作系统。 7. 自动代码生成:Keil5可以与STM32CubeMX配合使用,自动生成C代码,并将其导入到Keil5的工程中。 总的来说,STM32CubeMX和Keil5都是非常优秀的嵌入式开发工具,可以大大提高嵌入式开发的效率。其中STM32CubeMX主要用于芯片外设的图形化配置和C代码生成,而Keil5则主要用于代码编辑、编译、调试和仿真等方面。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值