1 <#
2 .SYNOPSIS
3     Azure RTOS ThreadX CI/CD script
4 
5 .DESCRIPTION
6     Entry point for automated building, cleaning, testing, verification,  ...
7 
8 .EXAMPLE
9     .\azrtos_cicd.ps1 -clean
10     This cleans all examples in the default database.
11 
12     .\azrtos_cicd.ps1 -MatchName 'Modules' -build
13     This builds all ThreadX Modules examples.
14 
15     .\azrtos_cicd.ps1 -MatchName 'Modules','IAR' -build
16     This builds all examples in the default database that have names matching the string 'Modules' and 'IAR'.
17 
18     .\azrtos_cicd.ps1 -MatchName 'Cortex M4','ARM compiler v6' -StartCLI
19     This opens CLI with set environments for all examples in the default database that have names matching the 'Cortex M4' and 'ARM compiler v6'.
20 
21 .LINK
22     https://azure.com/rtos
23 
24 .NOTES
25     Author: Andres Mlinar
26     Date:   2021
27 #>
28 
29 [CmdletBinding(PositionalBinding=$false)] Param(
30 
31     [string[]]
32     #One or more CSV files with the list of examples to process
33     $CsvFile = "azrtos_cicd.csv",
34 
35     [string[]]
36     #One or more regular expression strings to match names for
37     $MatchName,
38 
39     [string[]]
40     #One or more regular expression strings to match keywords for
41     $MatchKeywords,
42 
43     [string[]]
44     #One or more regular expression strings to match paths for
45     $MatchPath,
46 
47     [string]
48     #The source root directory, if not specified it defaults to the current repo
49     $SrcDir = $( Join-Path $PSScriptRoot "..\.." ),
50 
51     [string]
52     #The script root directory, if not specified it defaults to this script's directory
53     $ScriptDir = $PSScriptRoot,
54 
55     [string]
56     #The output log directory
57     $LogDir = $( Join-Path $PSScriptRoot "log" ),
58 
59     [switch]
60     #Start a CLI with a set environment for the specified examples
61     $StartCLI = $false,
62 
63     [switch]
64     #Check compliance for the specified examples
65     $CheckCompliance = $false,
66 
67     [switch]
68     #Clean all files not in the repository for the specified examples
69     $RealClean = $false,
70 
71     [switch]
72     #Clean the specified examples
73     $Clean = $false,
74 
75     [switch]
76     #Build the specified examples
77     $Build = $false,
78 
79     [switch]
80     #Test the specified examples
81     $Test = $false
82 
83 )
84 
85 If ($PSVersionTable.PSVersion.Major -lt 7) {
86     Write-Host "ERROR: PowerShell version 7 or higher is required!"
87     Exit -2
88 }
89 
90 Write-Verbose ("CSV files: " + $CsvFile)
91 Write-Verbose ("MatchName: " + $MatchName.Count + "[" + $MatchName + "]")
92 Write-Verbose ("MatchKeywords: " + $MatchKeywords.Count + "[" + $MatchKeywords + "]")
93 Write-Verbose ("MatchPath: " + $MatchPath.Count + "[" + $MatchPath + "]")
94 Write-Verbose ("SrcDir: " + $SrcDir)
95 Write-Verbose ("ScriptDir: " + $ScriptDir)
96 Write-Verbose ("LogDir: " + $LogDir)
97 Write-Verbose ("StartCLI? " + $StartCLI)
98 Write-Verbose ("CheckCompliance? " + $CheckCompliance)
99 Write-Verbose ("RealClean? " + $RealClean)
100 Write-Verbose ("Clean? " + $Clean)
101 Write-Verbose ("Build? " + $Build)
102 Write-Verbose ("Test? " + $Test)
103 
104 $Stats = [PSCustomObject]@{
105     Total               = 0
106     CurrentCsv          = 0
107     CleanScriptOK       = 0
108     CleanScriptERROR    = 0
109     BuildScriptOK       = 0
110     BuildScriptERROR    = 0
111     TestScriptOK        = 0
112     TestScriptERROR     = 0
113 }
114 
Invoke-CustomScriptnull115 function Invoke-CustomScript {
116 
117     Param (
118 
119         [string]
120         #A string describing the script type
121         $Type,
122 
123         [string]
124         #The sample name
125         $Name,
126 
127         [string]
128         #The environment setup script full path
129         $EnvScript,
130 
131         [string]
132         #The script full path
133         $Script
134     )
135 
136     Write-Verbose ("Type: " + $Type)
137     Write-Verbose ("SampleDir: " + $SampleDir)
138     Write-Verbose ("EnvScript: " + $EnvScript)
139     Write-Verbose ("Script: " + $Script)
140 
141     If (-not (Test-Path -Path $EnvScript -PathType leaf)) {
142         Write-Host "ERROR: the environment setup script doesn't exist:" $Script -ForegroundColor red
143         return -1
144     }
145 
146     If (-not (Test-Path -Path $Script -PathType leaf)) {
147         Write-Host "ERROR: the script doesn't exist:" $Script -ForegroundColor red
148         return -1
149     }
150 
151     Write-Host ("`n`r")
152     Write-Host (Get-Date -Format FileDateTimeUniversal) "INFO:" $Type "starting" "->" $Name -ForegroundColor green
153 
154     $ScriptOutput = & CMD /C "$EnvScript && $Script 2>&1"
155     $ExitCode = $LASTEXITCODE
156     Write-Verbose ("ExitCode: " + $ExitCode)
157     Write-Verbose ("ScriptOutput: " + $ScriptOutput)
158 
159     $LogFileName = (Get-Date -Format FileDateTimeUniversal) + '.' + ($Name -replace ' ', '_' -replace '/', '_' -replace '\\', '_') + "." + $Type + "." + (($ExitCode -eq 0) ? "OK" : "ERROR") + ".log"
160     $LogFileFullPath = Join-Path $LogDir $LogFileName
161     Write-Verbose ("LogFileNmae: " + $LogFileName)
162     Write-Verbose ("LogFileFullPath: " + $LogFileFullPath)
163 
164     $ScriptOutput | Out-File -FilePath $LogFileFullPath
165 
166     If ($ExitCode -ne 0) {
167         Write-Host (Get-Date -Format FileDateTimeUniversal) "ERROR:" $Type "failed!" "->" $Name -ForegroundColor red
168     } else {
169         Write-Host (Get-Date -Format FileDateTimeUniversal) "INFO:" $Type "success!" "->" $Name -ForegroundColor green
170     }
171 
172     return $ExitCode
173 }
174 
175 # Create the log directory if it doesn't already exists
176 If (-Not (Test-Path -Path $LogDir -PathType Container)) {
177     New-Item -Path $LogDir -ItemType Directory
178 }
179 
180 ForEach ($f in $CsvFile) {
181 
182     Write-Host $(Get-Date -Format FileDateTimeUniversal) "INFO:" $f -ForegroundColor green
183 
184     Write-Verbose ("CSV file: " + $f)
185 
186     try {
187         $t = Import-Csv -Path $f
188     } catch {
189         Write-Output "ERROR: failed to open CSV file:" $f -ForegroundColor red
190         Continue
191     }
192 
193     Write-Verbose ("Table rows: " + $t.Count)
194 
195     $Stats.CurrentCsv = 0
196 
197     ForEach ($i in $t) {
198 
199         $Stats.Total++
200         $Stats.CurrentCsv++
201 
202         Write-Verbose ("`n`r")
203         Write-Verbose ("Name: " + $i.Name)
204         Write-Verbose ("Keywords: " + $i.Keywords)
205         Write-Verbose ("Path: " + $i.Path)
206         Write-Verbose ("HostPlatform: " + $i.HostPlatform)
207 
208         If ($i.HostPlatform -ne $PSVersionTable.Platform) {
209             Write-Verbose ("HostPlatform mismatch, skipping...")
210             Continue
211         }
212 
213         $match = $true
214 
215         If ($MatchName.Count -ne 0) {
216             Write-Verbose ("Matching name...")
217             ForEach ($m in $MatchName) {
218                 Write-Verbose ("Matching " + $m)
219                 If (-not ($i.Name -match $m)) {
220                     Write-Verbose ("Name mismatch: " + $m)
221                     $match = $false;
222                 }
223             }
224         }
225 
226         If ($MatchKeywords.Count -ne 0) {
227             Write-Verbose ("Matching keywords...")
228             ForEach ($m in $MatchKeywords) {
229                 Write-Verbose ("Matching " + $m)
230                 If (-not ($i.Keywords -match $m)) {
231                     Write-Verbose ("Keywords mismatch: " + $m)
232                     $match = $false
233                 }
234             }
235         }
236 
237         If ($MatchPath.Count -ne 0) {
238             Write-Verbose ("Matching path...")
239             ForEach ($m in $MatchPath) {
240                 Write-Verbose ("Matching " + $m)
241                 If (-not ($i.Path -match $m)) {
242                     Write-Verbose ("Path mismatch: " + $m)
243                     $match = $false
244                 }
245             }
246         }
247 
248         If (-not $match) {
249             Write-Verbose ("No match, skipping...")
250             Continue
251         }
252 
253         $FullPath = Join-Path $SrcDir $i.Path
254         Write-Verbose ("FullPath: " + $FullPath)
255 
256         If (-Not $( Test-Path $FullPath -PathType Container )) {
257             Write-Host "ERROR: path doesn't exist:" $FullPath -ForegroundColor red
258             Continue
259         }
260 
261         If ($StartCLI) {
262             If ($PSVersionTable.Platform -eq "Win32NT") {
263                 Start-Process `
264                     -FilePath "cmd.exe" `
265                     -WorkingDirectory $FullPath `
266                     -ArgumentList (
267                         "/k " `
268                         + ("set PATH=" + $PSScriptRoot + ";%PATH% & ") `
269                         + ("DOSKEY realclean=" + "git clean -d -f -X " + $FullPath + " & ") `
270                         + ("DOSKEY clean=" + (Join-Path $ScriptDir $i.CleanEnvScript) + '$T$T' + (Join-Path $ScriptDir $i.CleanScript) + " & ") `
271                         + ("DOSKEY build=" + (Join-Path $ScriptDir $i.BuildEnvScript) + '$T$T' + (Join-Path $ScriptDir $i.BuildScript) + " & ") `
272                         + ("DOSKEY test=" + (Join-Path $ScriptDir $i.TestEnvScript) + '$T$T' + (Join-Path $ScriptDir $i.TestScript))
273                         )
274             }
275         }
276 
277         Push-Location $FullPath
278 
279         If ($RealClean ) {
280             & ("git clean -d -f -X " + $FullPath)
281         }
282 
283         If ($Clean -and ($i.CleanScript -ne "")) {
284             Write-Progress -Activity $f -Status ($Stats.CurrentCsv.ToString() + "/" + $t.Count.ToString()) -CurrentOperation $i.Name -PercentComplete (1.0 * $Stats.CurrentCsv / $t.Count * 100)
285             $result = Invoke-CustomScript "Clean" $i.Name (Join-Path $ScriptDir $i.CleanEnvScript) (Join-Path $ScriptDir $i.CleanScript)
286             if ($result -ne 0) {
287                 $Stats.CleanScriptERROR++
288             } else {
289                 $Stats.CleanScriptOK++
290             }
291         }
292 
293         If ($Build -and ($i.BuildScript -ne "")) {
294             Write-Progress -Activity $f -Status ($Stats.CurrentCsv.ToString() + "/" + $t.Count.ToString()) -CurrentOperation $i.Name -PercentComplete (1.0 * $Stats.CurrentCsv / $t.Count * 100)
295             $result = Invoke-CustomScript "Build" $i.Name (Join-Path $ScriptDir $i.BuildEnvScript) (Join-Path $ScriptDir $i.BuildScript)
296             if ($result -ne 0) {
297                 $Stats.BuildScriptERROR++
298             } else {
299                 $Stats.BuildScriptOK++
300             }
301         }
302 
303         If ($Test -and ($i.TestScript -ne "")) {
304             Write-Progress -Activity $f -Status ($Stats.CurrentCsv.ToString() + "/" + $t.Count.ToString()) -CurrentOperation $i.Name -PercentComplete (1.0 * $Stats.CurrentCsv / $t.Count * 100)
305             $result = Invoke-CustomScript "Test" $i.Name (Join-Path $ScriptDir $i.TestEnvScript) (Join-Path $ScriptDir $i.TestScript)
306             if ($result -ne 0) {
307                 $Stats.TestScriptERROR++
308             } else {
309                 $Stats.TestScriptOK++
310             }
311         }
312 
313         Pop-Location
314     }
315 }
316 
317 "Total: " + $Stats.Total
318 If ($Clean) {
319     "Clean: " + $Stats.CleanScriptOK + " OK, " + $Stats.CleanScriptERROR + " failed"
320 }
321 If ($Build) {
322     "Build: " + $Stats.BuildScriptOK + " OK, " + $Stats.BuildScriptERROR + " failed"
323 }
324 If ($Test) {
325     "Test: " + $Stats.TestScriptOK + " OK, " + $Stats.TestScriptERROR + " failed"
326 }
327 
328 Exit $Stats.CleanScriptERROR+$Stats.BuildScriptERROR+$Stats.TestScriptERROR
329