nuttx/tools/testbuild.ps1
simbit18 34c4b15b4d tools/ci: Added CI system on Windows Native
This PR adds support for the CI system for native Windows as well. It allows you to build NuttX on GitHub and test it locally for Windows users.

With these CI tools with PowerShell scripts, it is possible to build NuttX for Windows Native using (for now only) Cmake + Ninja with the same logic as the CI system with Bash scripts.

This allows the msvc job to be used not only with the simulator (currently only with Visual Studio 17 2022), but also with other architectures using the same Windows runner to get more coverage and avoid future breakage.
As with the other jobs, we use artifacts to save the compilation result at the end of the workflow execution (previously for the simulator it was not done).

The proposed solution is based on the following additions and modified:

Modified Files
buildyml -> only CI Jobs MSVC

New Files in tools/
ci/cibuild.ps1 -> Added Powershell script for Run the CI Builds
ci/platforms/windows.ps1 -> Added Powershell script for installing toolchains and tools.
testlist/windows.dat -> Target (Add sim (msvc), risc-v arm)
tools/testbuild.ps1

We tested the NuttX build on GitHub and locally.

How we build on GitHub and test locally.

Locally
cd .\nuttx\tools\ci\

.\cibuild.ps1 -n -i -A -C -N .\testlist\windows.dat

Signed-off-by: simbit18 <simbit18@gmail.com>
2025-03-19 19:59:44 +08:00

501 lines
13 KiB
PowerShell

#!/usr/bin/env pswd
############################################################################
# tools/testbuild.ps1
# PowerShell script for CI on Windows Native
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
# Set-PSDebug -Trace 0
$progname = $($MyInvocation.MyCommand.Name)
$WD = Resolve-Path("Get-Location\..\..\..\..")
$nuttx = "$WD\nuttx"
$global:fail = 0
$APPSDIR = "$WD\apps"
if ($null -eq $ARTIFACTDIR) {
$ARTIFACTDIR = "$WD\buildartifacts"
}
$PRINTLISTONLY = 0
$GITCLEAN = 0
$SAVEARTIFACTS = 0
$CHECKCLEAN = 1
$CODECHECKER = 0
$NINJACMAKE = 0
$RUN = 0
$MSVC = 0
function showusage {
Write-Host ""
Write-Host "USAGE: $progname -h [-n] [-A] [-G] [-N]"
Write-Host "Where:"
Write-Host " -h will show this help test and terminate"
Write-Host " -n selects Windows native"
Write-Host " -p only print the list of configs without running any builds"
Write-Host " -A store the build executable artifact in ARTIFACTDIR (defaults to ../buildartifacts"
Write-Host " -C Skip tree cleanness check."
Write-Host " -G Use `"git clean -xfdq`" instead of `"make distclean`" to clean the tree."
Write-Host " -N Use CMake with Ninja as the backend."
Write-Host " <testlist-file> selects the list of configurations to test. No default"
Write-Host ""
exit 1
}
# Define a function to search for Visual Studio 2022 installation
function Find-VisualStudio {
# Check common installation paths
$commonPaths = @(
"C:\Program Files\Microsoft Visual Studio\2022\Community",
"C:\Program Files\Microsoft Visual Studio\2022\Professional",
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
)
foreach ($path in $commonPaths) {
if (Test-Path $path) {
$MSVC = 1
}
}
if ($MSVC -eq 1) {
Write-Host "Found Visual Studio 2022 installations"
return $MSVC = 1
}
else {
Write-Host "Visual Studio 2022 is not installed on this system."
return $MSVC = 0
}
}
$MSVC = Find-VisualStudio
if (!$args[0]) {
showusage
}
for ( $i = 0; $i -lt $args.count; $i++ ) {
switch -regex -casesensitive ($($args[$i])) {
'-h' {
showusage
}
'-p' {
$PRINTLISTONLY = 1
}
'-G' {
$GITCLEAN = 1
}
'-A' {
$SAVEARTIFACTS = 1
}
'-C' {
$CHECKCLEAN = 0
}
'-N' {
$NINJACMAKE = 1
}
default {
Write-Host "File $($args[$i])" -ForegroundColor Green
$testfile = $($args[$i])
}
}
}
# Check if testfile file exists
if (-not (Test-Path -Path "$testfile")) {
Write-Host "ERROR: Missing test list file" -ForegroundColor Red
showusage
}
# Check if nuttx directory exists
if (-Not (Test-Path -Path $nuttx)) {
Write-Host "ERROR: Expected to find nuttx/ at $nuttx" -ForegroundColor Red
showusage
}
# Check if apps directory exists
if (-Not (Test-Path -Path $APPSDIR)) {
Write-Host "ERROR: No directory found at $APPSDIR" -ForegroundColor Red
exit 1
}
$patternallitem = '^(-|\\)|^[C|c][M|m][A|a][K|k][E|e]'
$patterntestlist = '^\\'
$patternblacklist = '^-'
if ($NINJACMAKE -eq 1) {
$patterncmakelist = '^[C|c][M|m][A|a][K|k][E|e][,]'
}
$content = Get-Content .\$testfile
$content = $content -replace '/', '\'
Set-Location "$nuttx"
$listfull = $content | Select-String $patternallitem -AllMatches
$testlist = $listfull | Select-String $patterntestlist -AllMatches
$blacklist = $listfull | Select-String $patternblacklist -AllMatches
if ($NINJACMAKE -eq 1) {
$cmakelist = $listfull | Select-String $patterncmakelist -AllMatches
}
# Clean up after the last build
function distclean {
Write-Host " Cleaning..."
if ((Test-Path ".config") -or (Test-Path "build\.config")) {
if (($GITCLEAN -eq 1) -or ($cmake)) {
git -C $nuttx clean -xfdq
git -C $APPSDIR clean -xfdq
}
else {
# Remove .version manually because this file is shipped with
# the release package and then distclean has to keep it.
if ($CHECKCLEAN -ne 0) {
if ((Test-Path -Path "$nuttx\.git") -or (Test-Path -Path "$APPSDIR\.git")) {
try {
if (git -C $nuttx status --ignored -s 2>$null) {
git -C $nuttx status --ignored -s
$global:fail = 1
}
if (git -C $APPSDIR status --ignored -s 2>$null) {
git -C $APPSDIR status --ignored -s
$global:fail = 1
}
}
catch {
Write-Host "Git is not installed. Please install Git to use this script." -ForegroundColor Red
$global:fail = 1
}
}
}
}
}
}
function run_command ($command) {
invoke-expression "$command" *>$null
Write-Host "run_command $_"
return $_
}
function configure_cmake {
# Run CMake with specified configurations
try {
$tmpconfig = $config -replace '\\', ':'
# cmake -B vs2022 -DBOARD_CONFIG=sim/windows -G"Visual Studio 17 2022" -A Win32
# cmake --build vs2022
if (($tmpconfig -match "windows") -and ($MSVC -eq 1)) {
if (cmake -B build -DBOARD_CONFIG="$tmpconfig" -G"Visual Studio 17 2022" -A Win32 1>$null) {
cmake -B build -DBOARD_CONFIG="$tmpconfig" -G"Visual Studio 17 2022" -A Win32
$global:fail = 1
}
}
else {
if (cmake -B build -DBOARD_CONFIG="$tmpconfig" -GNinja 1> $null) {
cmake -B build -DBOARD_CONFIG="$tmpconfig" -GNinja
Write-Host "cmake -B build -DBOARD_CONFIG=$tmpconfig -GNinja"
$global:fail = 1
}
}
Write-Host " CMake configuration completed successfully."
}
catch {
Write-Host " CMake configuration failed: $_"
$global:fail = 1
}
if ($toolchain) {
$patternallitem = '_TOOLCHAIN_'
$contentconfig = Get-Content "$nuttx\build\.config"
$listtoolchain = $contentconfig | Select-String $patternallitem -AllMatches
$listtoolchain = $listtoolchain | Select-String 'CONFIG_TOOLCHAIN_WINDOWS', 'CONFIG_ARCH_TOOLCHAIN_*' -NotMatch | Select-String '=y' -AllMatches
$toolchainarr = $listtoolchain -split '='
$original_toolchain = $($toolchainarr[0])
if ($original_toolchain) {
Write-Host " Disabling $original_toolchain"
kconfig-tweak.ps1 --file "$nuttx\build\.config" -d $original_toolchain
}
Write-Host " Enabling $toolchain"
kconfig-tweak.ps1 --file "$nuttx\build\.config" -e $toolchain
}
}
function configure {
Write-Host " Configuring..."
if ($cmake) {
configure_cmake
}
else {
# configure_default to-do
Write-Host " configure_default" -ForegroundColor Green
}
}
# Perform the next build
function build_default {
# make build_default to-do
}
function build_cmake {
# Build the project
try {
if (cmake --build build 1> $null) {
cmake --build build
$global:fail = 1
}
Write-Host " Build completed successfully."
}
catch {
Write-Error "Build failed: $_"
$global:fail = 1
}
if ($SAVEARTIFACTS -eq 1) {
$artifactconfigdir = "$ARTIFACTDIR\$config"
# Write-Host "Copy in artifactconfigdir: $artifactconfigdir"
New-Item -Force -ItemType directory -Path $artifactconfigdir > $null
try {
$contentmanifest = $null
$contentmanifest = Get-Content "$nuttx\build\nuttx.manifest"
$tmpconfig = $config -replace '\\', ':'
if (($tmpconfig -match "windows") -and ($MSVC -eq 1)) {
Copy-Item -Path "$nuttx\build\Debug\nuttx.exe" -Destination $artifactconfigdir -Force -ErrorAction Stop
Copy-Item -Path "$nuttx\build\Debug\nuttx.pdb" -Destination $artifactconfigdir -Force -ErrorAction Stop
}
else {
foreach ($ma in $contentmanifest) {
Copy-Item -Path "$nuttx\build\$ma" -Destination $artifactconfigdir -Force -ErrorAction Stop
}
}
}
catch {
Write-Host " An error occurred while copying files: $_" -ForegroundColor Red
}
}
}
function build {
Write-Host " Building NuttX..."
if ($cmake) {
Write-Host " build_cmake" -ForegroundColor Green
build_cmake
}
else {
# make build_default to-do
Write-Host " build_default to-do" -ForegroundColor Green
}
}
function refresh_default {
# make refresh_default to-do
}
function refresh_cmake {
# Ensure defconfig in the canonical form
if ($toolchain) {
if ($original_toolchain) {
kconfig-tweak.ps1 --file "$nuttx\build\.config" -e $original_toolchain
}
kconfig-tweak.ps1 --file "$nuttx\build\.config" -d $toolchain
}
try {
if (cmake --build build -t refreshsilent 1> $null) {
cmake --build build -t refreshsilent
$global:fail = 1
}
Write-Host " Refresh completed successfully."
}
catch {
Write-Error "refresh failed: $_"
$global:fail = 1
}
try {
Remove-Item build -Recurse -Force
}
catch {
Write-Error "Remove-Item failed: $_"
}
# Ensure nuttx and apps directory in clean state
if ($CHECKCLEAN -ne 0) {
if ((Test-Path -Path "$nuttx\.git") -or (Test-Path -Path "$APPSDIR\.git")) {
try {
if (git -C $nuttx status -s 2> $null) {
Write-Host "Git $nuttx status " -ForegroundColor Yellow
git -C $nuttx status -s
$global:fail = 1
}
if (git -C $APPSDIR status -s 2> $null) {
Write-Host "Git $APPSDIR status " -ForegroundColor Yellow
git -C $APPSDIR status -s
$global:fail = 1
}
}
catch {
Write-Host "Git is not installed. Please install Git to use this script." -ForegroundColor Red
$global:fail = 1
}
}
}
# Use -f option twice to remove git sub-repository
git -C $nuttx clean -f -xfdq
git -C $APPSDIR clean -f -xfdq
}
function refresh {
if ($cmake) {
refresh_cmake
}
else {
# make refresh_default to-do
}
}
function run {
# run to-do
}
# Coordinate the steps for the next build test
function dotest {
param (
[string]$configfull
)
Write-Host "===================================================================================="
$configarr = $configfull -split ','
$config = $($configarr[0])
$check = "Windows," + $config -replace '\\', ':'
$skip = 0
foreach ($re in $blacklist) {
if ("-$check" -eq "$re") {
$skip = 1
}
}
$cmake = $null
if ($NINJACMAKE -eq 1) {
foreach ($l in $cmakelist) {
if ("Cmake," + $config -replace '\\', ':' -eq "$l") {
$cmake = 1
}
}
}
Write-Host "Configuration/Tool: $configfull" -ForegroundColor Yellow
if ($PRINTLISTONLY -eq 1) {
return
}
$tmparr = $config -split '\\'
$configdir = $($tmparr[1])
$boarddir = $($tmparr[0]).Trim()
if (($boarddir -eq "sim") -and ($MSVC -ne 1)) {
$skip = 1
}
$path = "$nuttx\boards\*\*\$boarddir\configs\$configdir"
if (-Not (Test-Path -Path $path)) {
Write-Host "ERROR: no configuration found at $path" -ForegroundColor Red
showusage
}
$toolchain = $null
$original_toolchain = $null
if ($config -ne $configfull) {
$toolchain = $($configarr[1])
}
Write-Host (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") -ForegroundColor Yellow
Write-Host "------------------------------------------------------------------------------------"
distclean
if ($skip -ne 1 ) {
configure
build
refresh
}
else {
Write-Host " Skipping: $config" -ForegroundColor Green
}
}
foreach ($line in $testlist) {
$firstch = [string[]]$line | ForEach-Object { $_[0] }
$arr = $line -split ','
if ($firstch -eq '\') {
$dir = $arr
if (!$dir[1]) {
$cnftoolchain = ""
}
else {
$cnftoolchain = "," + $dir[1]
}
$filePath = Get-ChildItem -Path "boards$($dir[0])" -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq "defconfig" }
if ($filePath) {
foreach ($i in $filePath) {
$arrpath = $($i.FullName) -split '\\'
$list = "$($arrpath[$arrpath.count - 4])\$($arrpath[$arrpath.count - 2])"
dotest "$list$cnftoolchain"
}
}
else {
Write-Host "File '$FileName' not found in '$list'." -ForegroundColor Yellow
}
}
else {
dotest "$line"
}
}
Write-Host "===================================================================================="
exit $global:fail