diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..517657b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@github.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8765977 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +## Contributing + +[fork]: https://github.com/actions/boost-versions/fork +[pr]: https://github.com/actions/boost-versions/compare +[code-of-conduct]: CODE_OF_CONDUCT.md + +Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [MIT](LICENSE.md). + +Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms. + +## Submitting a pull request + +1. [Fork][fork] and clone the repository +1. Create a new branch: `git checkout -b my-branch-name` +1. Make your changes +1. Push to your fork and [submit a pull request][pr] +1. Make sure that checks in your pull request are green + +Here are a few things you can do that will increase the likelihood of your pull request being accepted: + +- Please include a summary of the change and which issue is fixed. Also include relevant motivation and context. +- Follow the style guide for [PowerShell](https://github.com/PoshCode/PowerShellPracticeAndStyle). +- Write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +## Code structure + +### Directory structure +``` + +├── azure-pipelines/ +| └──templates/ +├── builders/ +├── helpers/ +├── installers/ +└── tests/ + └──sources/ +``` +- `azure-pipelines*` - contains global YAML definitions for build pipelines. Reusable templates for specific jobs are located in `templates` subfolder. +- `builders` - contains Boost builder classes and functions. +- `helpers` - contains global helper functions and functions. +- `installers` - contains installation script templates. +- `tests` - contains test scripts. Required tests sources are located in `sources` subfolder. + +\* _We use Azure Pipelines because there are a few features that Actions is still missing, we'll move to Actions as soon as possible_. + +## Resources + +- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) +- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) +- [GitHub Help](https://help.github.com) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f42f5ab --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 8b13789..979e339 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ +# Boost for Actions +This repository contains the code and scripts that we use to prepare Boost packages used in [virtual-environments](https://github.com/actions/virtual-environments). +The file [versions-manifest.json](./versions-manifest.json) contains the list of available and released versions. +> Caution: this is prepared for and only permitted for use by actions `virtual-environments`. + +**Status**: Currently under development and in use for beta and preview actions. This repo is undergoing rapid changes. + +## Adding new versions +We are trying to prepare packages for new versions of Boost as soon as they are released. Please open an issue if any versions are missing. + +## Contribution +Contributions are welcome! See [Contributor's Guide](./CONTRIBUTING.md) for more details about contribution process and code structure diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..f0b196f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github) + +Thanks for helping make GitHub Actions safe for everyone. diff --git a/azure-pipelines/build-boost-packages.yml b/azure-pipelines/build-boost-packages.yml new file mode 100644 index 0000000..65ad96f --- /dev/null +++ b/azure-pipelines/build-boost-packages.yml @@ -0,0 +1,95 @@ +name: $(date:yyyyMMdd)$(rev:.r)-Boost-$(VERSION) +trigger: none +pr: + autoCancel: true + branches: + include: + - master + paths: + exclude: + - versions-manifest.json + +stages: +- stage: Build_Boost_Ubuntu_1604 + dependsOn: [] + variables: + VmImage: ubuntu-16.04 + Platform: linux-16.04 + Architecture: x64 + Toolset: gcc + jobs: + - template: /azure-pipelines/templates/build-job.yml + +- stage: Test_Boost_Ubuntu_1604 + condition: succeeded() + dependsOn: Build_Boost_Ubuntu_1604 + variables: + VmImage: ubuntu-16.04 + Platform: linux-16.04 + Architecture: x64 + Toolset: gcc + jobs: + - template: /azure-pipelines/templates/test-job.yml + +- stage: Build_Boost_Ubuntu_1804 + dependsOn: [] + variables: + VmImage: 'ubuntu-18.04' + Platform: linux-18.04 + Architecture: x64 + Toolset: gcc + jobs: + - template: /azure-pipelines/templates/build-job.yml + +- stage: Test_Boost_Ubuntu_1804 + condition: succeeded() + dependsOn: Build_Boost_Ubuntu_1804 + variables: + VmImage: 'ubuntu-18.04' + Platform: linux-18.04 + Architecture: x64 + Toolset: gcc + jobs: + - template: /azure-pipelines/templates/test-job.yml + +- stage: Build_Boost_msvc141 + dependsOn: [] + variables: + VmImage: 'vs2017-win2016' + Platform: win32 + Architecture: x86_64 + Toolset: msvc-14.1 + jobs: + - template: /azure-pipelines/templates/build-job.yml + +- stage: Test_Boost_msvc141 + condition: succeeded() + dependsOn: Build_Boost_msvc141 + variables: + VmImage: 'vs2017-win2016' + Platform: win32 + Architecture: x86_64 + Toolset: msvc-14.1 + jobs: + - template: /azure-pipelines/templates/test-job.yml + +- stage: Build_Boost_msvc142 + dependsOn: [] + variables: + VmImage: 'windows-2019' + Platform: win32 + Architecture: x86_64 + Toolset: msvc-14.2 + jobs: + - template: /azure-pipelines/templates/build-job.yml + +- stage: Test_Boost_msvc142 + condition: succeeded() + dependsOn: Build_Boost_msvc142 + variables: + VmImage: 'windows-2019' + Platform: win32 + Architecture: x86_64 + Toolset: msvc-14.2 + jobs: + - template: /azure-pipelines/templates/test-job.yml \ No newline at end of file diff --git a/azure-pipelines/templates/build-job.yml b/azure-pipelines/templates/build-job.yml new file mode 100644 index 0000000..4de7c9b --- /dev/null +++ b/azure-pipelines/templates/build-job.yml @@ -0,0 +1,20 @@ +jobs: +- job: Build_Boost + timeoutInMinutes: 300 + pool: + name: Azure Pipelines + vmImage: $(VmImage) + steps: + - checkout: self + - task: PowerShell@2 + displayName: 'Build Boost $(Version)' + inputs: + targetType: filePath + filePath: './builders/build-boost.ps1' + arguments: '-Version $(Version) -Platform $(Platform) -Architecture $(Architecture) -Toolset $(Toolset)' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: Boost $(Version)' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)' + artifactName: 'boost-$(Version)-$(Platform)-$(Toolset)-$(Architecture)' \ No newline at end of file diff --git a/azure-pipelines/templates/test-job.yml b/azure-pipelines/templates/test-job.yml new file mode 100644 index 0000000..fa0805f --- /dev/null +++ b/azure-pipelines/templates/test-job.yml @@ -0,0 +1,80 @@ +jobs: +- job: Test_Boost + pool: + name: Azure Pipelines + vmImage: $(VmImage) + steps: + - checkout: self + submodules: true + - task: PowerShell@2 + displayName: Fully cleanup the toolcache directory before testing + inputs: + targetType: filePath + filePath: helpers/clean-toolcache.ps1 + arguments: -ToolName "boost" + + - task: DownloadPipelineArtifact@2 + inputs: + source: 'current' + artifact: 'boost-$(Version)-$(Platform)-$(Toolset)-$(Architecture)' + path: $(Build.ArtifactStagingDirectory) + + - task: ExtractFiles@1 + inputs: + archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/boost-$(Version)-$(Platform)-*-$(Architecture).*' + destinationFolder: $(Build.BinariesDirectory) + cleanDestinationFolder: false + + - task: PowerShell@2 + displayName: 'Apply build artifact to the local machines' + inputs: + targetType: inline + script: | + if ("$(Platform)" -match 'win32') { powershell ./setup.ps1 } else { sh ./setup.sh } + workingDirectory: '$(Build.BinariesDirectory)' + + - task: PowerShell@2 + displayName: 'Set up BOOST_ROOT' + inputs: + TargetType: inline + script: | + $BoostToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath "boost" + $BoostToolcacheVersionPath = Join-Path -Path $BoostToolcachePath -ChildPath "$(Version)" + $boostDirectory = Join-Path $BoostToolcacheVersionPath "$(Architecture)" + $LD_LIBRARY = Join-Path -Path $boostDirectory -ChildPath "lib" + Write-Host "BOOST_ROOT = ${boostDirectory}" + Write-Host "##vso[task.setvariable variable=BOOST_ROOT]${boostDirectory}" + Write-Host "##vso[task.setvariable variable=LD_LIBRARY_PATH]${LD_LIBRARY}" + + - task: PowerShell@2 + displayName: 'Run tests' + inputs: + TargetType: inline + script: | + Install-Module Pester -Force -Scope CurrentUser + Import-Module Pester + $Platform = If ("$(Platform)" -eq "win32") { "Windows" } else { "Nix" } + $pesterParams = @( + @{ + Path="./Common.Tests.ps1"; + }, + @{ + Path="./${Platform}.Tests.ps1"; + Parameters=@{ + Version="$(Version)"; + Platform="$(Platform)" + } + } + ) + Invoke-Pester -Script $pesterParams -OutputFile "$(Build.SourcesDirectory)/tests/test_results.xml" -OutputFormat NUnitXml + workingDirectory: '$(Build.SourcesDirectory)/tests' + + - task: PublishTestResults@2 + displayName: 'Publish test results' + inputs: + testResultsFiles: '*.xml' + testResultsFormat: NUnit + searchFolder: 'tests' + failTaskOnFailedTests: true + testRunTitle: "Boost $(Version)-$(Platform)-$(Toolset)" + condition: always() diff --git a/builders/boost-builder.psm1 b/builders/boost-builder.psm1 new file mode 100644 index 0000000..c5be369 --- /dev/null +++ b/builders/boost-builder.psm1 @@ -0,0 +1,90 @@ +class BoostBuilder { + <# + .SYNOPSIS + Base Boost builder class. + + .DESCRIPTION + Base Boost builder class that contains general builder methods. + + .PARAMETER Version + The version of Boost that should be built. + + .PARAMETER Platform + The platform of Boost that should be built. + + .PARAMETER Architecture + The architecture with which Boost should be built. + + .PARAMETER TempFolderLocation + The location of temporary files that will be used during Boost package generation. Using system BUILD_STAGINGDIRECTORY variable value. + + .PARAMETER ArtifactLocation + The location of generated Boost artifact. Using system environment BUILD_BINARIESDIRECTORY variable value. + + .PARAMETER InstallationTemplatesLocation + The location of installation script template. Using "installers" folder from current repository. + + #> + + [version] $Version + [string] $Platform + [string] $Architecture + [string] $Toolset + [string] $TempFolderLocation + [string] $WorkFolderLocation + [string] $ArtifactFolderLocation + [string] $InstallationTemplatesLocation + + BoostBuilder ([version] $version, [string] $platform, [string] $architecture, [string] $toolset) { + $this.Version = $version + $this.Platform = $platform + $this.Architecture = $architecture + $this.Toolset = $toolset + + $this.TempFolderLocation = [IO.Path]::GetTempPath() + $this.WorkFolderLocation = $env:BUILD_BINARIESDIRECTORY + $this.ArtifactFolderLocation = $env:BUILD_STAGINGDIRECTORY + + $this.InstallationTemplatesLocation = Join-Path -Path $PSScriptRoot -ChildPath "../installers" + } + + [void] Download() { + <# + .SYNOPSIS + Download Boost source code. + #> + + $gitArguments = @( + "clone" + "https://github.com/boostorg/boost.git", + $this.WorkFolderLocation, + "--branch", "boost-$($this.Version)", + "--single-branch", + "--recursive" + ) -join " " + Execute-Command "git $gitArguments" + + Write-Host "Removing .git subfolder to reduce artifact size..." + $gitFolder = Join-Path $this.WorkFolderLocation ".git" + Remove-Item $gitFolder -Recurse -Force + } + + [void] Build() { + <# + .SYNOPSIS + Generates Boost artifact from downloaded binaries. + #> + + Write-Host "Download Boost $($this.Version) source code..." + $this.Download() + + Write-Host "Build source code..." + $this.Make() + + Write-Host "Create installation script..." + $this.CreateInstallationScript() + + Write-Host "Archive artifact..." + $this.ArchiveArtifact() + } +} diff --git a/builders/build-boost.ps1 b/builders/build-boost.ps1 new file mode 100644 index 0000000..0779ae2 --- /dev/null +++ b/builders/build-boost.ps1 @@ -0,0 +1,75 @@ +using module "./builders/win-boost-builder.psm1" +using module "./builders/nix-boost-builder.psm1" + +<# +.SYNOPSIS +Generate Boost artifact. + +.DESCRIPTION +Main script that creates instance of BoostBuilder and builds of Boost using specified parameters. + +.PARAMETER Version +Required parameter. The version with which Boost will be built. + +.PARAMETER Architecture +Optional parameter. The architecture with which Boost will be built. Using x64 by default. + +.PARAMETER Platform +Required parameter. The platform for which Boost will be built. + +#> + +param( + [Parameter (Mandatory=$true)][Version] $Version, + [Parameter (Mandatory=$true)][string] $Platform, + [Parameter (Mandatory=$true)][string] $Architecture, + [Parameter (Mandatory=$true)][string] $Toolset +) + +Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "common-helpers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "nix-helpers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "win-helpers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "win-vs-env.psm1") -DisableNameChecking + +$ErrorActionPreference = "Stop" + +function Get-BoostBuilder { + <# + .SYNOPSIS + Wrapper for class constructor to simplify importing BoostBuilder. + + .DESCRIPTION + Create instance of BoostBuilder with specified parameters. + + .PARAMETER Version + The version with which Boost will be built. + + .PARAMETER Platform + The platform for which Boost will be built. + + .PARAMETER Architecture + The architecture with which Boost will be built. + + #> + + param ( + [version] $Version, + [string] $Architecture, + [string] $Platform + ) + + if ($Platform -match 'win32') { + $builder = [WinBoostBuilder]::New($Version, $Platform, $Architecture, $Toolset) + } elseif ($Platform -match 'linux') { + $builder = [NixBoostBuilder]::New($Version, $Platform, $Architecture, $Toolset) + } else { + Write-Host "##vso[task.logissue type=error;] Invalid platform: $Platform" + exit 1 + } + + return $builder +} + +### Create Boost builder instance, and build artifact +$Builder = Get-BoostBuilder -Version $Version -Platform $Platform -Architecture $Architecture +$Builder.Build() diff --git a/builders/nix-boost-builder.psm1 b/builders/nix-boost-builder.psm1 new file mode 100644 index 0000000..dda6e31 --- /dev/null +++ b/builders/nix-boost-builder.psm1 @@ -0,0 +1,77 @@ +using module "./builders/boost-builder.psm1" + +class NixBoostBuilder : BoostBuilder { + <# + .SYNOPSIS + Ubuntu Boost builder class. + + .DESCRIPTION + Contains methods that required to build Ubuntu Boost artifact from sources. Inherited from base BoostBuilder. + + #> + + [string] $InstallationTemplateName + [string] $InstallationScriptName + [string] $OutputArtifactName + + NixBoostBuilder( + [version] $version, + [string] $platform, + [string] $architecture, + [string] $toolset + ) : Base($version, $platform, $architecture, $toolset) { + $this.InstallationTemplateName = "nix-setup-template.sh" + $this.InstallationScriptName = "setup.sh" + $this.OutputArtifactName = "boost-$Version-$Platform-$Toolset-$Architecture.tar.gz" + } + + [void] Make() { + Push-Location -Path $this.WorkFolderLocation + + Write-Host "Invoke bootstrap.sh" + Execute-Command "sudo ./bootstrap.sh" + + $commonArguments = @( + "install" + "--prefix=$($this.WorkFolderLocation)", + "--build-dir=$($this.TempFolderLocation)", + "--layout='tagged'", + "link='static,shared'", + "runtime-link='static,shared'" + ) -join " " + + Write-Host "Build boost static and shared binaries in release" + Execute-Command "sudo ./b2 $commonArguments variant='release'" + + Write-Host "Build boost static and shared binaries in debug" + Execute-Command "sudo ./b2 $commonArguments variant='debug'" + + Pop-Location + } + + [void] CreateInstallationScript() { + <# + .SYNOPSIS + Create Boost artifact installation script based on template specified in InstallationTemplateName property. + #> + + $installationScriptLocation = New-Item -Path $this.WorkFolderLocation -Name $this.InstallationScriptName -ItemType File + $installationTemplateLocation = Join-Path -Path $this.InstallationTemplatesLocation -ChildPath $this.InstallationTemplateName + + $installationTemplateContent = Get-Content -Path $installationTemplateLocation -Raw + $variablesToReplace = @{ + "{{__VERSION__}}" = $this.Version; + "{{__ARCHITECTURE__}}" = "x64"; + } + + $variablesToReplace.keys | ForEach-Object { $installationTemplateContent = $installationTemplateContent.Replace($_, $variablesToReplace[$_]) } + $installationTemplateContent | Out-File -FilePath $installationScriptLocation + + Write-Debug "Done; Installation script location: $installationScriptLocation)" + } + + [void] ArchiveArtifact() { + $OutputPath = Join-Path $this.ArtifactFolderLocation $this.OutputArtifactName + Create-TarArchive -SourceFolder $this.WorkFolderLocation -ArchivePath $OutputPath + } +} \ No newline at end of file diff --git a/builders/win-boost-builder.psm1 b/builders/win-boost-builder.psm1 new file mode 100644 index 0000000..d87693b --- /dev/null +++ b/builders/win-boost-builder.psm1 @@ -0,0 +1,105 @@ +using module "./builders/boost-builder.psm1" + + +class WinBoostBuilder : BoostBuilder { + <# + .SYNOPSIS + Ubuntu Boost builder class. + + .DESCRIPTION + Contains methods that required to build Ubuntu Boost artifact from sources. Inherited from base NixBoostBuilder. + + .PARAMETER Toolset + The toolset which well be used to buil source code on windows vs. + + #> + + [string] $InstallationTemplateName + [string] $InstallationScriptName + [string] $OutputArtifactName + + WinBoostBuilder( + [version] $version, + [string] $platform, + [string] $architecture, + [string] $toolset + ) : Base($version, $platform, $architecture, $toolset) { + $this.InstallationTemplateName = "win-setup-template.ps1" + $this.InstallationScriptName = "setup.ps1" + $toolsetPart = $toolset.Replace("-", "") + $this.OutputArtifactName = "boost-$Version-$Platform-$toolsetPart-$Architecture.tar.gz" + } + + [void] CreateIncludeSymlink() { + $includeFolder = "$($this.WorkFolderLocation)\include" + $headerDestination = "$($this.WorkFolderLocation)\include\boost" + if ((Test-Path $includeFolder) -and (-not(Test-Path $headerDestination))) { + Write-Host "Move headers to root" + $headersSource = Get-Childitem $includeFolder | Where-Object { $_.PsIsContainer } | Select-Object -First 1 -ExpandProperty FullName + Copy-Item -Path "${headersSource}\boost" -Destination $headerDestination -Recurse -Container + } + + if (-not (Test-Path -Path "$($this.WorkFolderLocation)\bjam.exe")) { + Copy-Item -Path "$($this.WorkFolderLocation)\b2.exe" -Destination "$($this.WorkFolderLocation)\bjam.exe" + } + } + + [void] Make() { + Write-Host "Initialize VS dev environment" + Invoke-VSDevEnvironment + + Push-Location -Path $this.WorkFolderLocation + + Write-Host "Invoke bootstrap.sh" + Execute-Command "./bootstrap.bat msvc" + + Write-Host "Build boost with '$($this.Toolset)' toolset..." + $commonArguments = @( + "install", + "--prefix='$($this.WorkFolderLocation)'", + "--build-dir='$($this.TempFolderLocation)'", + "variant='debug,release'", + "link='static,shared'", + "runtime-link='static,shared'", + "address-model='32,64'", + "toolset='$($this.Toolset)'", + "-j4" + ) -join " " + Execute-Command "./b2 $commonArguments" -ErrorAction Continue + + $this.CreateIncludeSymlink() + + Pop-Location + } + + [void] CreateInstallationScript() { + <# + .SYNOPSIS + Create Boost artifact installation script based on specified template. + #> + + $installationScriptLocation = New-Item -Path $this.WorkFolderLocation -Name $this.InstallationScriptName -ItemType File + $installationTemplateLocation = Join-Path -Path $this.InstallationTemplatesLocation -ChildPath $this.InstallationTemplateName + $installationTemplateContent = Get-Content -Path $installationTemplateLocation -Raw + + $variablesToReplace = @{ + "{{__VERSION__}}" = $this.Version; + "{{__ARCHITECTURE__}}" = $this.Architecture; + } + + $variablesToReplace.keys | ForEach-Object { $installationTemplateContent = $installationTemplateContent.Replace($_, $variablesToReplace[$_]) } + $installationTemplateContent | Out-File -FilePath $installationScriptLocation + Write-Debug "Done; Installation script location: $installationScriptLocation)" + } + + [void] ArchiveArtifact() { + $archiveTempDir = (New-Item -Name "tempArchive" -ItemType Directory -Path $this.TempFolderLocation).Fullname + $TempTarArchive = [IO.Path]::GetFileNameWithoutExtension($this.OutputArtifactName) + $OutPathTempTar = Join-Path -Path $archiveTempDir -ChildPath $TempTarArchive + $OutputPath = Join-Path -Path $this.ArtifactFolderLocation -ChildPath $this.OutputArtifactName + Write-Host "Pack to tar" + Create-SevenZipArchive -SourceFolder $this.WorkFolderLocation -ArchivePath $OutPathTempTar -ArchiveType "tar" -IncludeSymlinks + Write-Host "Pack to tar.gz" + Create-SevenZipArchive -SourceFolder $archiveTempDir -ArchivePath $OutputPath -ArchiveType "gzip" + } +} diff --git a/config/boost-manifest-config.json b/config/boost-manifest-config.json new file mode 100644 index 0000000..d4c1705 --- /dev/null +++ b/config/boost-manifest-config.json @@ -0,0 +1,9 @@ +{ + "regex": "boost-\\d+\\.\\d+\\.\\d+-(\\w+)-([\\d\\.]+)?-?([\\w\\.]+)-(x[\\d_]+)", + "groups": { + "arch": 4, + "platform": 1, + "platform_version": 2, + "toolset": 3 + } +} \ No newline at end of file diff --git a/helpers/azure-devops/azure-devops-api.ps1 b/helpers/azure-devops/azure-devops-api.ps1 new file mode 100644 index 0000000..dd2feba --- /dev/null +++ b/helpers/azure-devops/azure-devops-api.ps1 @@ -0,0 +1,89 @@ +class AzureDevOpsApi +{ + [string] $BaseUrl + [string] $RepoOwner + [object] $AuthHeader + + AzureDevOpsApi( + [string] $TeamFoundationCollectionUri, + [string] $ProjectName, + [string] $AccessToken + ) { + $this.BaseUrl = $this.BuildBaseUrl($TeamFoundationCollectionUri, $ProjectName) + $this.AuthHeader = $this.BuildAuth($AccessToken) + } + + [object] hidden BuildAuth([string]$AccessToken) { + if ([string]::IsNullOrEmpty($AccessToken)) { + return $null + } + return @{ + Authorization = "Bearer $AccessToken" + } + } + + [string] hidden BuildBaseUrl([string]$TeamFoundationCollectionUri, [string]$ProjectName) { + return "${TeamFoundationCollectionUri}/${ProjectName}/_apis" + } + + [object] QueueBuild([string]$ToolVersion, [string]$SourceBranch, [string]$SourceVersion, [UInt32]$DefinitionId){ + $url = "build/builds" + + # The content of parameters field should be a json string + $buildParameters = @{ VERSION = $ToolVersion } | ConvertTo-Json + + $body = @{ + definition = @{ + id = $DefinitionId + } + sourceBranch = $SourceBranch + sourceVersion = $SourceVersion + parameters = $buildParameters + } | ConvertTo-Json + + return $this.InvokeRestMethod($url, 'POST', $body) + } + + [object] GetBuildInfo([UInt32]$BuildId){ + $url = "build/builds/$BuildId" + + return $this.InvokeRestMethod($url, 'GET', $null) + } + + [string] hidden BuildUrl([string]$Url) { + return "$($this.BaseUrl)/${Url}/?api-version=5.1" + } + + [object] hidden InvokeRestMethod( + [string] $Url, + [string] $Method, + [string] $Body + ) { + $requestUrl = $this.BuildUrl($Url) + $params = @{ + Method = $Method + ContentType = "application/json" + Uri = $requestUrl + Headers = @{} + } + if ($this.AuthHeader) { + $params.Headers += $this.AuthHeader + } + if (![string]::IsNullOrEmpty($body)) { + $params.Body = $Body + } + + return Invoke-RestMethod @params + } + +} + +function Get-AzureDevOpsApi { + param ( + [string] $TeamFoundationCollectionUri, + [string] $ProjectName, + [string] $AccessToken + ) + + return [AzureDevOpsApi]::New($TeamFoundationCollectionUri, $ProjectName, $AccessToken) +} \ No newline at end of file diff --git a/helpers/azure-devops/build-info.ps1 b/helpers/azure-devops/build-info.ps1 new file mode 100644 index 0000000..9622d8c --- /dev/null +++ b/helpers/azure-devops/build-info.ps1 @@ -0,0 +1,44 @@ +Import-Module (Join-Path $PSScriptRoot "azure-devops-api.ps1") + +class BuildInfo +{ + [AzureDevOpsApi] $AzureDevOpsApi + [String] $Name + [UInt32] $Id + [String] $Status + [String] $Result + [String] $Link + + BuildInfo([AzureDevOpsApi] $AzureDevOpsApi, [object] $Build) + { + $this.AzureDevOpsApi = $AzureDevOpsApi + $this.Id = $Build.id + $this.Name = $Build.buildNumber + $this.Link = $Build._links.web.href + $this.Status = $Build.status + $this.Result = $Build.result + } + + [boolean] IsFinished() { + return ($this.Status -eq "completed") -or ($this.Status -eq "cancelling") + } + + [boolean] IsSuccess() { + return $this.Result -eq "succeeded" + } + + [void] UpdateBuildInfo() { + $buildInfo = $this.AzureDevOpsApi.GetBuildInfo($this.Id) + $this.Status = $buildInfo.status + $this.Result = $buildInfo.result + } +} + +function Get-BuildInfo { + param ( + [AzureDevOpsApi] $AzureDevOpsApi, + [object] $Build + ) + + return [BuildInfo]::New($AzureDevOpsApi, $Build) +} \ No newline at end of file diff --git a/helpers/azure-devops/run-ci-builds.ps1 b/helpers/azure-devops/run-ci-builds.ps1 new file mode 100644 index 0000000..bf11295 --- /dev/null +++ b/helpers/azure-devops/run-ci-builds.ps1 @@ -0,0 +1,94 @@ +param ( + [Parameter(Mandatory)] [string] $TeamFoundationCollectionUri, + [Parameter(Mandatory)] [string] $AzureDevOpsProjectName, + [Parameter(Mandatory)] [string] $AzureDevOpsAccessToken, + [Parameter(Mandatory)] [string] $SourceBranch, + [Parameter(Mandatory)] [string] $ToolVersions, + [Parameter(Mandatory)] [UInt32] $DefinitionId, + [string] $SourceVersion +) + +Import-Module (Join-Path $PSScriptRoot "azure-devops-api.ps1") +Import-Module (Join-Path $PSScriptRoot "build-info.ps1") + +function Queue-Builds { + param ( + [Parameter(Mandatory)] [AzureDevOpsApi] $AzureDevOpsApi, + [Parameter(Mandatory)] [string] $ToolVersions, + [Parameter(Mandatory)] [string] $SourceBranch, + [Parameter(Mandatory)] [string] $SourceVersion, + [Parameter(Mandatory)] [string] $DefinitionId + ) + + [BuildInfo[]]$queuedBuilds = @() + + $ToolVersions.Split(',') | ForEach-Object { + $version = $_.Trim() + Write-Host "Queue build for $version..." + $queuedBuild = $AzureDevOpsApi.QueueBuild($version, $SourceBranch, $SourceVersion, $DefinitionId) + $buildInfo = Get-BuildInfo -AzureDevOpsApi $AzureDevOpsApi -Build $queuedBuild + Write-Host "Queued build: $($buildInfo.Link)" + $queuedBuilds += $buildInfo + } + + return $queuedBuilds +} + +function Wait-Builds { + param ( + [Parameter(Mandatory)] [BuildInfo[]] $Builds + ) + + $timeoutBetweenRefreshSec = 30 + + do { + # If build is still running - refresh its status + foreach($build in $builds) { + if (!$build.IsFinished()) { + $build.UpdateBuildInfo() + + if ($build.IsFinished()) { + Write-Host "The $($build.Name) build was completed: $($build.Link)" + } + } + } + + $runningBuildsCount = ($builds | Where-Object { !$_.IsFinished() }).Length + + Start-Sleep -Seconds $timeoutBetweenRefreshSec + } while($runningBuildsCount -gt 0) +} + +function Make-BuildsOutput { + param ( + [Parameter(Mandatory)] [BuildInfo[]] $Builds + ) + + Write-Host "Builds info:" + $builds | Format-Table -AutoSize -Property Name,Id,Status,Result,Link | Out-String -Width 10000 + + # Return exit code based on status of builds + $failedBuilds = ($builds | Where-Object { !$_.IsSuccess() }) + if ($failedBuilds.Length -ne 0) { + Write-Host "##vso[task.logissue type=error;]Builds failed" + $failedBuilds | ForEach-Object -Process { Write-Host "##vso[task.logissue type=error;]Name: $($_.Name); Link: $($_.Link)" } + Write-Host "##vso[task.complete result=Failed]" + } else { + Write-host "##[section] All builds have been passed successfully" + } +} + +$azureDevOpsApi = Get-AzureDevOpsApi -TeamFoundationCollectionUri $TeamFoundationCollectionUri ` + -ProjectName $AzureDevOpsProjectName ` + -AccessToken $AzureDevOpsAccessToken + +$queuedBuilds = Queue-Builds -AzureDevOpsApi $azureDevOpsApi ` + -ToolVersions $ToolVersions ` + -SourceBranch $SourceBranch ` + -SourceVersion $SourceVersion ` + -DefinitionId $DefinitionId + +Write-Host "Waiting results of builds ..." +Wait-Builds -Builds $queuedBuilds + +Make-BuildsOutput -Builds $queuedBuilds diff --git a/helpers/clean-toolcache.ps1 b/helpers/clean-toolcache.ps1 new file mode 100644 index 0000000..67796de --- /dev/null +++ b/helpers/clean-toolcache.ps1 @@ -0,0 +1,13 @@ +param ( + [string] $ToolName +) + +$targetPath = $env:AGENT_TOOLSDIRECTORY +if ($ToolName) { + $targetPath = Join-Path $targetPath $ToolName +} + +if (Test-Path $targetPath) { + Get-ChildItem -Path $targetPath -Recurse | Where-Object { $_.LinkType -eq "SymbolicLink" } | ForEach-Object { $_.Delete() } + Remove-Item -Path $targetPath -Recurse -Force +} diff --git a/helpers/common-helpers.psm1 b/helpers/common-helpers.psm1 new file mode 100644 index 0000000..cbc7665 --- /dev/null +++ b/helpers/common-helpers.psm1 @@ -0,0 +1,85 @@ +<# +.SYNOPSIS +The execute command and print all output to the logs +#> +function Execute-Command { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string] $Command + ) + + Write-Debug "Execute $Command" + + try { + Invoke-Expression $Command | ForEach-Object { Write-Host $_ } + if ($LASTEXITCODE -ne 0) { throw "Exit code: $LASTEXITCODE"} + } + catch { + $errorMessage = "Error happened during command execution: $Command `n $_" + Write-Host $errorMessage + if ($ErrorActionPreference -ne "Continue") { + # avoid logging Azure DevOps issues in case of $ErrorActionPreference -eq Continue + Write-Host "##vso[task.logissue type=error;] $errorMessage" + } + } +} + +<# +.SYNOPSIS +Download file from url and return local path to file +#> +function Download-File { + param( + [Parameter(Mandatory=$true)] + [Uri]$Uri, + [Parameter(Mandatory=$true)] + [String]$OutputFolder + ) + + $targetFilename = [IO.Path]::GetFileName($Uri) + $targetFilepath = Join-Path $OutputFolder $targetFilename + + Write-Debug "Download source from $Uri to $OutFile" + try { + (New-Object System.Net.WebClient).DownloadFile($Uri, $targetFilepath) + return $targetFilepath + } catch { + Write-Host "Error during downloading file from '$Uri'" + "$_" + exit 1 + } +} + +<# +.SYNOPSIS +Generate file that contains the list of all files in particular directory +#> +function New-ToolStructureDump { + param( + [Parameter(Mandatory=$true)] + [String]$ToolPath, + [Parameter(Mandatory=$true)] + [String]$OutputFolder + ) + + $outputFile = Join-Path $OutputFolder "tools_structure.txt" + + $folderContent = Get-ChildItem -Path $ToolPath -Recurse | Sort-Object | Select-Object -Property FullName, Length + $folderContent | ForEach-Object { + $relativePath = $_.FullName.Replace($ToolPath, ""); + return "${relativePath}" + } | Out-File -FilePath $outputFile +} + +<# +.SYNOPSIS +Check if it is macOS / Ubuntu platform +#> +function IsNixPlatform { + param( + [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] + [String]$Platform + ) + + return ($Platform -match "macos") -or ($Platform -match "ubuntu") -or ($Platform -match "linux") +} \ No newline at end of file diff --git a/helpers/github/create-pull-request.ps1 b/helpers/github/create-pull-request.ps1 new file mode 100644 index 0000000..9c01042 --- /dev/null +++ b/helpers/github/create-pull-request.ps1 @@ -0,0 +1,106 @@ +<# +.SYNOPSIS +Create commit with all unstaged changes in repository and create pull-request + +.PARAMETER RepositoryOwner +Required parameter. The organization which tool repository belongs +.PARAMETER RepositoryName +Optional parameter. The name of tool repository +.PARAMETER AccessToken +Required parameter. PAT Token to authorize +.PARAMETER BranchName +Required parameter. The name of branch where changes will be pushed +.PARAMETER CommitMessage +Required parameter. The commit message to push changes +.PARAMETER PullRequestTitle +Required parameter. The title of pull-request +.PARAMETER PullRequestBody +Required parameter. The description of pull-request +#> +param ( + [Parameter(Mandatory)] [string] $RepositoryOwner, + [Parameter(Mandatory)] [string] $RepositoryName, + [Parameter(Mandatory)] [string] $AccessToken, + [Parameter(Mandatory)] [string] $BranchName, + [Parameter(Mandatory)] [string] $CommitMessage, + [Parameter(Mandatory)] [string] $PullRequestTitle, + [Parameter(Mandatory)] [string] $PullRequestBody +) + +Import-Module (Join-Path $PSScriptRoot "github-api.psm1") +Import-Module (Join-Path $PSScriptRoot "git.psm1") + +function Update-PullRequest { + Param ( + [Parameter(Mandatory=$true)] + [object] $GitHubApi, + [Parameter(Mandatory=$true)] + [string] $Title, + [Parameter(Mandatory=$true)] + [string] $Body, + [Parameter(Mandatory=$true)] + [string] $BranchName, + [Parameter(Mandatory=$true)] + [object] $PullRequest + ) + + $updatedPullRequest = $GitHubApi.UpdatePullRequest($Title, $Body, $BranchName, $PullRequest.number) + + if (($updatedPullRequest -eq $null) -or ($updatedPullRequest.html_url -eq $null)) { + Write-Host "##vso[task.logissue type=error;] Unexpected error occurs while updating pull request." + exit 1 + } + Write-host "##[section] Pull request updated: $($updatedPullRequest.html_url)" +} + +function Create-PullRequest { + Param ( + [Parameter(Mandatory=$true)] + [object] $GitHubApi, + [Parameter(Mandatory=$true)] + [string] $Title, + [Parameter(Mandatory=$true)] + [string] $Body, + [Parameter(Mandatory=$true)] + [string] $BranchName + ) + + $createdPullRequest = $GitHubApi.CreateNewPullRequest($Title, $Body, $BranchName) + + if (($createdPullRequest -eq $null) -or ($createdPullRequest.html_url -eq $null)) { + Write-Host "##vso[task.logissue type=error;] Unexpected error occurs while creating pull request." + exit 1 + } + + Write-host "##[section] Pull request created: $($createdPullRequest.html_url)" +} + +Write-Host "Configure local git preferences" +Git-ConfigureUser -Name "Service account" -Email "no-reply@microsoft.com" + +Write-Host "Create branch: $BranchName" +Git-CreateBranch -Name $BranchName + +Write-Host "Create commit" +Git-CommitAllChanges -Message $CommitMessage + +Write-Host "Push branch: $BranchName" +Git-PushBranch -Name $BranchName -Force $true + +$gitHubApi = Get-GitHubApi -AccountName $RepositoryOwner -ProjectName $RepositoryName -AccessToken $AccessToken +$pullRequest = $gitHubApi.GetPullRequest($BranchName, $RepositoryOwner) + +if ($pullRequest.Count -gt 0) { + Write-Host "Update pull request" + Update-PullRequest -GitHubApi $gitHubApi ` + -Title $PullRequestTitle ` + -Body $PullRequestBody ` + -BranchName $BranchName ` + -PullRequest $pullRequest[0] +} else { + Write-Host "Create pull request" + Create-PullRequest -GitHubApi $gitHubApi ` + -Title $PullRequestTitle ` + -Body $PullRequestBody ` + -BranchName $BranchName +} diff --git a/helpers/github/git.psm1 b/helpers/github/git.psm1 new file mode 100644 index 0000000..2383a79 --- /dev/null +++ b/helpers/github/git.psm1 @@ -0,0 +1,81 @@ +<# +.SYNOPSIS +Configure git credentials to use with commits +#> +function Git-ConfigureUser { + Param ( + [Parameter(Mandatory=$true)] + [string] $Name, + [Parameter(Mandatory=$true)] + [string] $Email + ) + + git config --global user.name $Name | Out-Host + git config --global user.email $Email | Out-Host + + if ($LASTEXITCODE -ne 0) { + Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while configuring git preferences." + exit 1 + } +} + +<# +.SYNOPSIS +Create new branch +#> +function Git-CreateBranch { + Param ( + [Parameter(Mandatory=$true)] + [string] $Name + ) + + git checkout -b $Name | Out-Host + + if ($LASTEXITCODE -ne 0) { + Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while creating new branch: $Name." + exit 1 + } +} + +<# +.SYNOPSIS +Commit all staged and unstaged changes +#> +function Git-CommitAllChanges { + Param ( + [Parameter(Mandatory=$true)] + [string] $Message + ) + + git add -A | Out-Host + git commit -m "$Message" | Out-Host + + if ($LASTEXITCODE -ne 0) { + Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while commiting changes." + exit 1 + } +} + +<# +.SYNOPSIS +Push branch to remote repository +#> +function Git-PushBranch { + Param ( + [Parameter(Mandatory=$true)] + [string] $Name, + [Parameter(Mandatory=$true)] + [boolean] $Force + ) + + if ($Force) { + git push --set-upstream origin $Name --force | Out-Host + } else { + git push --set-upstream origin $Name | Out-Host + } + + if ($LASTEXITCODE -ne 0) { + Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while pushing changes." + exit 1 + } +} \ No newline at end of file diff --git a/helpers/github/github-api.psm1 b/helpers/github/github-api.psm1 new file mode 100644 index 0000000..2634423 --- /dev/null +++ b/helpers/github/github-api.psm1 @@ -0,0 +1,126 @@ +<# +.SYNOPSIS +The module that contains a bunch of methods to interact with GitHub API V3 +#> +class GitHubApi +{ + [string] $BaseUrl + [string] $RepoOwner + [object] $AuthHeader + + GitHubApi( + [string] $AccountName, + [string] $ProjectName, + [string] $AccessToken + ) { + $this.BaseUrl = $this.BuildBaseUrl($AccountName, $ProjectName) + $this.AuthHeader = $this.BuildAuth($AccessToken) + } + + [object] hidden BuildAuth([string]$AccessToken) { + if ([string]::IsNullOrEmpty($AccessToken)) { + return $null + } + $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("'':${AccessToken}")) + return @{ + Authorization = "Basic ${base64AuthInfo}" + } + } + + [string] hidden BuildBaseUrl([string]$RepositoryOwner, [string]$RepositoryName) { + return "https://api.github.com/repos/$RepositoryOwner/$RepositoryName" + } + + [object] CreateNewPullRequest([string]$Title, [string]$Body, [string]$BranchName){ + $requestBody = @{ + title = $Title + body = $Body + head = $BranchName + base = "master" + } | ConvertTo-Json + + $url = "pulls" + return $this.InvokeRestMethod($url, 'Post', $null, $requestBody) + } + + [object] GetPullRequest([string]$BranchName, [string]$RepositoryOwner){ + $url = "pulls" + return $this.InvokeRestMethod($url, 'GET', "head=${RepositoryOwner}:$BranchName&base=master", $null) + } + + [object] UpdatePullRequest([string]$Title, [string]$Body, [string]$BranchName, [string]$PullRequestNumber){ + $requestBody = @{ + title = $Title + body = $Body + head = $BranchName + base = "master" + } | ConvertTo-Json + + $url = "pulls/$PullRequestNumber" + return $this.InvokeRestMethod($url, 'Post', $null, $requestBody) + } + + [array] GetReleases(){ + $url = "releases" + $releases = @() + $pageNumber = 1 + $releaseNumberLimit = 10000 + + while ($releases.Count -le $releaseNumberLimit) + { + $requestParams = "page=${pageNumber}&per_page=100" + [array] $response = $this.InvokeRestMethod($url, 'GET', $requestParams, $null) + + if ($response.Count -eq 0) { + break + } else { + $releases += $response + $pageNumber++ + } + } + + return $releases + } + + [string] hidden BuildUrl([string]$Url, [string]$RequestParams) { + if ([string]::IsNullOrEmpty($RequestParams)) { + return "$($this.BaseUrl)/$($Url)" + } else { + return "$($this.BaseUrl)/$($Url)?$($RequestParams)" + } + } + + [object] hidden InvokeRestMethod( + [string] $Url, + [string] $Method, + [string] $RequestParams, + [string] $Body + ) { + $requestUrl = $this.BuildUrl($Url, $RequestParams) + $params = @{ + Method = $Method + ContentType = "application/json" + Uri = $requestUrl + Headers = @{} + } + if ($this.AuthHeader) { + $params.Headers += $this.AuthHeader + } + if (![string]::IsNullOrEmpty($Body)) { + $params.Body = $Body + } + + return Invoke-RestMethod @params + } + +} + +function Get-GitHubApi { + param ( + [string] $AccountName, + [string] $ProjectName, + [string] $AccessToken + ) + + return [GitHubApi]::New($AccountName, $ProjectName, $AccessToken) +} \ No newline at end of file diff --git a/helpers/nix-helpers.psm1 b/helpers/nix-helpers.psm1 new file mode 100644 index 0000000..0f00e21 --- /dev/null +++ b/helpers/nix-helpers.psm1 @@ -0,0 +1,44 @@ +<# +.SYNOPSIS +Unpack *.tar file +#> +function Extract-TarArchive { + param( + [Parameter(Mandatory=$true)] + [String]$ArchivePath, + [Parameter(Mandatory=$true)] + [String]$OutputDirectory + ) + + Write-Debug "Extract $ArchivePath to $OutputDirectory" + tar -C $OutputDirectory -xzf $ArchivePath --strip 1 +} + +function Create-TarArchive { + param( + [Parameter(Mandatory=$true)] + [String]$SourceFolder, + [Parameter(Mandatory=$true)] + [String]$ArchivePath, + [string]$CompressionType = "gz", + [switch]$DereferenceSymlinks + ) + + $arguments = @( + "-c", + "-f", $ArchivePath, + "." + ) + If ($CompressionType) { + $arguments += "--${CompressionType}" + } + + if ($DereferenceSymlinks) { + $arguments += "-h" + } + + Push-Location $SourceFolder + Write-Debug "tar $arguments" + tar @arguments + Pop-Location +} \ No newline at end of file diff --git a/helpers/packages-generation/manifest-generator.ps1 b/helpers/packages-generation/manifest-generator.ps1 new file mode 100644 index 0000000..85434d0 --- /dev/null +++ b/helpers/packages-generation/manifest-generator.ps1 @@ -0,0 +1,35 @@ +<# +.SYNOPSIS +Generate versions manifest based on repository releases + +.DESCRIPTION +Versions manifest is needed to find the latest assets for particular version of tool +.PARAMETER GitHubRepositoryOwner +Required parameter. The organization which tool repository belongs +.PARAMETER GitHubRepositoryName +Optional parameter. The name of tool repository +.PARAMETER GitHubAccessToken +Required parameter. PAT Token to overcome GitHub API Rate limit +.PARAMETER OutputFile +Required parameter. File "*.json" where generated results will be saved +.PARAMETER ConfigurationFile +Path to the json file with parsing configuration +#> + +param ( + [Parameter(Mandatory)] [string] $GitHubRepositoryOwner, + [Parameter(Mandatory)] [string] $GitHubRepositoryName, + [Parameter(Mandatory)] [string] $GitHubAccessToken, + [Parameter(Mandatory)] [string] $OutputFile, + [Parameter(Mandatory)] [string] $ConfigurationFile +) + +Import-Module (Join-Path $PSScriptRoot "../github/github-api.psm1") +Import-Module (Join-Path $PSScriptRoot "manifest-utils.psm1") -Force + +$configuration = Read-ConfigurationFile -Filepath $ConfigurationFile + +$gitHubApi = Get-GitHubApi -AccountName $GitHubRepositoryOwner -ProjectName $GitHubRepositoryName -AccessToken $GitHubAccessToken +$releases = $gitHubApi.GetReleases() +$versionIndex = Build-VersionsManifest -Releases $releases -Configuration $configuration +$versionIndex | ConvertTo-Json -Depth 5 | Out-File $OutputFile -Encoding UTF8NoBOM -Force diff --git a/helpers/packages-generation/manifest-utils.Tests.ps1 b/helpers/packages-generation/manifest-utils.Tests.ps1 new file mode 100644 index 0000000..f1aa785 --- /dev/null +++ b/helpers/packages-generation/manifest-utils.Tests.ps1 @@ -0,0 +1,116 @@ +Import-Module (Join-Path $PSScriptRoot "manifest-utils.psm1") -Force + +Describe "New-AssetItem" { + It "use regex to parse all values in correct order" { + $githubAsset = @{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "long_url"; } + $configuration = @{ + regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)"; + groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = 3; }; + } + $expectedOutput = [PSCustomObject]@{ + filename = "python-3.8.3-linux-16.04-x64.tar.gz"; platform = "linux"; platform_version = "16.04"; + arch = "x64"; download_url = "long_url"; + } + + $actualOutput = New-AssetItem -ReleaseAsset $githubAsset -Configuration $configuration + Assert-Equivalent -Actual $actualOutput -Expected $expectedOutput + } + + It "support constant values in groups" { + $githubAsset = @{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "long_url"; } + $configuration = @{ + regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)"; + groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = "x64"; } + } + $expectedOutput = [PSCustomObject]@{ + filename = "python-3.8.3-linux-16.04-x64.tar.gz"; platform = "linux"; platform_version = "16.04"; + arch = "x64"; download_url = "long_url"; + } + + $actualOutput = New-AssetItem -ReleaseAsset $githubAsset -Configuration $configuration + Assert-Equivalent -Actual $actualOutput -Expected $expectedOutput + } + + It "Skip empty groups" { + $githubAsset = @{ name = "python-3.8.3-win32-x64.zip"; browser_download_url = "long_url"; } + $configuration = @{ + regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)"; + groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = 3; } + } + $expectedOutput = [PSCustomObject]@{ + filename = "python-3.8.3-win32-x64.zip"; platform = "win32"; + arch = "x64"; download_url = "long_url"; + } + + $actualOutput = New-AssetItem -ReleaseAsset $githubAsset -Configuration $configuration + Assert-Equivalent -Actual $actualOutput -Expected $expectedOutput + } +} + +Describe "Get-VersionFromRelease" { + It "clear version" { + $release = @{ name = "3.8.3" } + Get-VersionFromRelease -Release $release | Should -Be "3.8.3" + } + + It "version with title" { + $release = @{ name = "3.8.3: Release title" } + Get-VersionFromRelease -Release $release | Should -Be "3.8.3" + } +} + +Describe "Build-VersionsManifest" { + $assets = @( + @{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "fake_url"; } + @{ name = "python-3.8.3-linux-18.04-x64.tar.gz"; browser_download_url = "fake_url"; } + ) + $configuration = @{ + regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)"; + groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = "x64"; } + } + $expectedManifestFiles = @( + [PSCustomObject]@{ filename = "python-3.8.3-linux-16.04-x64.tar.gz"; arch = "x64"; platform = "linux"; platform_version = "16.04"; download_url = "fake_url" }, + [PSCustomObject]@{ filename = "python-3.8.3-linux-18.04-x64.tar.gz"; arch = "x64"; platform = "linux"; platform_version = "18.04"; download_url = "fake_url" } + ) + + It "build manifest with correct version order" { + $releases = @( + @{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-14T09:54:06Z"; assets = $assets }, + @{ name = "3.5.2: Hello"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:45:36Z"; assets = $assets }, + @{ name = "3.8.3: Release title"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:43:38Z"; assets = $assets } + ) + $expectedManifest = @( + [PSCustomObject]@{ version = "3.8.3"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles }, + [PSCustomObject]@{ version = "3.8.1"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles }, + [PSCustomObject]@{ version = "3.5.2"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles } + ) + $actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration + Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest + } + + It "Skip draft and prerelease" { + $releases = @( + @{ name = "3.8.1"; draft = $true; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-14T09:54:06Z"; assets = $assets }, + @{ name = "3.5.2"; draft = $false; prerelease = $true; html_url = "fake_html_url"; published_at = "2020-05-06T11:45:36Z"; assets = $assets }, + @{ name = "3.8.3"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:43:38Z"; assets = $assets } + ) + $expectedManifest = @( + [PSCustomObject]@{ version = "3.8.3"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles } + ) + [array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration + Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest + } + + It "take latest published release for each version" { + $releases = @( + @{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url1"; published_at = "2020-05-06T11:45:36Z"; assets = $assets }, + @{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url2"; published_at = "2020-05-14T09:54:06Z"; assets = $assets }, + @{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url3"; published_at = "2020-05-06T11:43:38Z"; assets = $assets } + ) + $expectedManifest = @( + [PSCustomObject]@{ version = "3.8.1"; stable = $true; release_url = "fake_html_url2"; files = $expectedManifestFiles } + ) + [array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration + Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest + } +} \ No newline at end of file diff --git a/helpers/packages-generation/manifest-utils.psm1 b/helpers/packages-generation/manifest-utils.psm1 new file mode 100644 index 0000000..2d4ef00 --- /dev/null +++ b/helpers/packages-generation/manifest-utils.psm1 @@ -0,0 +1,77 @@ +function Read-ConfigurationFile { + param ([Parameter(Mandatory)][string]$Filepath) + return Get-Content $Filepath -Raw | ConvertFrom-Json +} + +function New-AssetItem { + param ( + [Parameter(Mandatory)][object]$ReleaseAsset, + [Parameter(Mandatory)][object]$Configuration + ) + $regexResult = [regex]::Match($ReleaseAsset.name, $Configuration.regex) + if (-not $regexResult.Success) { throw "Can't match asset filename '$($_.name)' to regex" } + + $result = New-Object PSObject + $result | Add-Member -Name "filename" -Value $ReleaseAsset.name -MemberType NoteProperty + $Configuration.groups.PSObject.Properties | ForEach-Object { + if (($_.Value).GetType().Name.StartsWith("Int")) { + $value = $regexResult.Groups[$_.Value].Value + } else { + $value = $_.Value + } + + if (-not ([string]::IsNullOrEmpty($value))) { + $result | Add-Member -Name $_.Name -Value $value -MemberType NoteProperty + } + } + + $result | Add-Member -Name "download_url" -Value $ReleaseAsset.browser_download_url -MemberType NoteProperty + return $result +} + +function Get-VersionFromRelease { + param ( + [Parameter(Mandatory)][object]$Release + ) + # Release name can contain additional information after ':' so filter it + [string]$releaseName = $Release.name.Split(':')[0] + [Version]$version = $null + if (![Version]::TryParse($releaseName, [ref]$version)) { + throw "Release '$($Release.id)' has invalid title '$($Release.name)'. It can't be parsed as version. ( $($Release.html_url) )" + } + + return $version +} + +function Build-VersionsManifest { + param ( + [Parameter(Mandatory)][array]$Releases, + [Parameter(Mandatory)][object]$Configuration + ) + + $Releases = $Releases | Sort-Object -Property "published_at" -Descending + + $versionsHash = @{} + foreach ($release in $Releases) { + if (($release.draft -eq $true) -or ($release.prerelease -eq $true)) { + continue + } + + [Version]$version = Get-VersionFromRelease $release + $versionKey = $version.ToString() + + if ($versionsHash.ContainsKey($versionKey)) { + continue + } + + $versionsHash.Add($versionKey, [PSCustomObject]@{ + version = $versionKey + stable = $true + release_url = $release.html_url + files = $release.assets | ForEach-Object { New-AssetItem -ReleaseAsset $_ -Configuration $Configuration } + }) + } + + # Sort versions by descending + return $versionsHash.Values | Sort-Object -Property @{ Expression = { [Version]$_.version }; Descending = $true } +} \ No newline at end of file diff --git a/helpers/pester-extensions.psm1 b/helpers/pester-extensions.psm1 new file mode 100644 index 0000000..46d2a75 --- /dev/null +++ b/helpers/pester-extensions.psm1 @@ -0,0 +1,33 @@ +<# +.SYNOPSIS +Pester extension that allows to run command and validate exit code +.EXAMPLE +"python file.py" | Should -ReturnZeroExitCode +#> +function ShouldReturnZeroExitCode { + Param( + [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] + [String]$ActualValue, + [switch]$Negate + ) + + Write-Host "Run command '${ActualValue}'" + Invoke-Expression -Command $ActualValue | ForEach-Object { Write-Host $_ } + $actualExitCode = $LASTEXITCODE + + [bool]$succeeded = $actualExitCode -eq 0 + if ($Negate) { $succeeded = -not $succeeded } + + if (-not $succeeded) + { + $failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}" + } + + return New-Object PSObject -Property @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +Add-AssertionOperator -Name ReturnZeroExitCode ` + -Test $function:ShouldReturnZeroExitCode diff --git a/helpers/win-helpers.psm1 b/helpers/win-helpers.psm1 new file mode 100644 index 0000000..beac0b9 --- /dev/null +++ b/helpers/win-helpers.psm1 @@ -0,0 +1,39 @@ +<# +.SYNOPSIS +Unpack *.7z file +#> +function Extract-SevenZipArchive { + param( + [Parameter(Mandatory=$true)] + [String]$ArchivePath, + [Parameter(Mandatory=$true)] + [String]$OutputDirectory + ) + + Write-Debug "Extract $ArchivePath to $OutputDirectory" + 7z x $ArchivePath -o"$OutputDirectory" -y | Out-Null +} + +function Create-SevenZipArchive { + param( + [Parameter(Mandatory=$true)] + [String]$SourceFolder, + [Parameter(Mandatory=$true)] + [String]$ArchivePath, + [String]$ArchiveType = "zip", + [String]$CompressionLevel = 5, + [switch]$IncludeSymlinks + ) + + $ArchiveTypeArguments = @( + "-t${ArchiveType}", + "-mx=${CompressionLevel}" + ) + if ($IncludeSymlinks) { + $ArchiveTypeArguments += "-snl" + } + Push-Location $SourceFolder + Write-Debug "7z a $ArchiveTypeArgument $ArchivePath @$SourceFolder" + 7z a @ArchiveTypeArguments $ArchivePath $SourceFolder\* + Pop-Location +} \ No newline at end of file diff --git a/helpers/win-vs-env.psm1 b/helpers/win-vs-env.psm1 new file mode 100644 index 0000000..1eb9093 --- /dev/null +++ b/helpers/win-vs-env.psm1 @@ -0,0 +1,48 @@ +### +# Visual Studio helper functions +### + +function Get-VSWhere { + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"; + + if (-not (Test-Path $vswhere )) { + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + $vswhere = ".\vswhere.exe" + $vswhereApiUri = "https://api.github.com/repos/Microsoft/vswhere/releases/latest" + $tag = (Invoke-RestMethod -Uri $vswhereApiUri)[0].tag_name + $vswhereUri = "https://github.com/Microsoft/vswhere/releases/download/$tag/vswhere.exe" + Invoke-WebRequest -Uri $vswhereUri -OutFile $vswhere | Out-Null + } + + return $vswhere +} + +function Invoke-Environment +{ + Param + ( + [Parameter(Mandatory)] + [string] + $Command + ) + + & "${env:COMSPEC}" /s /c "`"$Command`" -no_logo && set" | Foreach-Object { + if ($_ -match '^([^=]+)=(.*)') { + [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2]) + } + } +} + +function Get-VSInstallationPath { + $vswhere = Get-VSWhere + $installationPath = & $vswhere -prerelease -legacy -latest -property installationPath + + return $installationPath +} + +function Invoke-VSDevEnvironment { + Write-Host "Invoke-VSDevEnvironment had been invoked" + $installationPath = Get-VSInstallationPath + $envFilepath = Join-Path $installationPath "Common7\Tools\vsdevcmd.bat" + Invoke-Environment -Command $envFilepath +} \ No newline at end of file diff --git a/installers/nix-setup-template.sh b/installers/nix-setup-template.sh new file mode 100644 index 0000000..bf285f3 --- /dev/null +++ b/installers/nix-setup-template.sh @@ -0,0 +1,33 @@ +set -e + +BOOST_VERSION="{{__VERSION__}}" +ARCHITECTURE="{{__ARCHITECTURE__}}" + +if [ -z ${AGENT_TOOLSDIRECTORY+x} ]; then + # No AGENT_TOOLSDIRECTORY on GitHub images + TOOLCACHE_ROOT=$RUNNER_TOOL_CACHE +else + TOOLCACHE_ROOT=$AGENT_TOOLSDIRECTORY +fi + +BOOST_TOOLCACHE_PATH=$TOOLCACHE_ROOT/boost +BOOST_TOOLCACHE_VERSION_PATH=$BOOST_TOOLCACHE_PATH/$BOOST_VERSION +BOOST_TOOLCACHE_VERSION_ARCH_PATH=$BOOST_TOOLCACHE_VERSION_PATH/$ARCHITECTURE + +echo "Check if Boost hostedtoolcache folder exist..." +if [ ! -d $BOOST_TOOLCACHE_PATH ]; then + mkdir -p $BOOST_TOOLCACHE_PATH +fi + +echo "Delete Boost $BOOST_VERSION [$ARCHITECTURE] if installed" +rm -rf $BOOST_TOOLCACHE_VERSION_ARCH_PATH + +echo "Create Boost $BOOST_VERSION [$ARCHITECTURE] folder" +mkdir -p $BOOST_TOOLCACHE_VERSION_ARCH_PATH + +echo "Copy Boost binaries to hostedtoolcache folder" +cp -R ./* $BOOST_TOOLCACHE_VERSION_ARCH_PATH +rm $BOOST_TOOLCACHE_VERSION_ARCH_PATH/setup.sh + +echo "Create complete file" +touch $BOOST_TOOLCACHE_VERSION_PATH/$ARCHITECTURE.complete diff --git a/installers/win-setup-template.ps1 b/installers/win-setup-template.ps1 new file mode 100644 index 0000000..b91bcc1 --- /dev/null +++ b/installers/win-setup-template.ps1 @@ -0,0 +1,33 @@ +$ErrorActionPreference = "Stop" + +[Version]$Version = "{{__VERSION__}}" +[string]$Architecture = "{{__ARCHITECTURE__}}" + +$ToolcacheRoot = $env:AGENT_TOOLSDIRECTORY +if ([string]::IsNullOrEmpty($ToolcacheRoot)) { + # GitHub images don't have `AGENT_TOOLSDIRECTORY` variable + $ToolcacheRoot = $env:RUNNER_TOOL_CACHE +} +$BoostToolcachePath = Join-Path -Path $ToolcacheRoot -ChildPath "boost" +$BoostToolcacheVersionPath = Join-Path -Path $BoostToolcachePath -ChildPath $Version.ToString() +$BoostToolcacheArchitecturePath = Join-Path $BoostToolcacheVersionPath $Architecture + +Write-Host "Check if Boost hostedtoolcache folder exist..." +if (-not (Test-Path $BoostToolcachePath)) { + New-Item -ItemType Directory -Path $BoostToolcachePath | Out-Null +} + +if (Test-Path $BoostToolcacheArchitecturePath) { + Write-Host "Delete existing Boost $Version [$Architecture]" + Remove-Item $BoostToolcacheArchitecturePath -Recurse -Force | Out-Null +} + +Write-Host "Create Boost $Version [$Architecture] folder" +New-Item -ItemType Directory -Path $BoostToolcacheArchitecturePath | Out-Null + +Write-Host "Copy Boost binaries to hostedtoolcache folder" +Copy-Item -Path * -Destination $BoostToolcacheArchitecturePath -Recurse +Remove-Item $BoostToolcacheArchitecturePath\setup.ps1 -Force | Out-Null + +Write-Host "Create complete file" +New-Item -ItemType File -Path $BoostToolcacheVersionPath -Name "$Architecture.complete" | Out-Null \ No newline at end of file diff --git a/tests/Common.Tests.ps1 b/tests/Common.Tests.ps1 new file mode 100644 index 0000000..7344917 --- /dev/null +++ b/tests/Common.Tests.ps1 @@ -0,0 +1,15 @@ +Describe "Common Tests" { + $BOOST_HEADERS = Join-Path -Path ${env:BOOST_ROOT} -ChildPath "boost" + + It "Check that symlinks points to existing files" { + Get-ChildItem $BOOST_HEADERS -File -Recurse | Where-Object { $_.Target } | ForEach-Object { + $_.Target | Should -Exist + } + } + + It "Check if there is no invalid symlinks" { + Get-ChildItem $BOOST_HEADERS -Recurse -File | ForEach-Object { + Get-Content $_.FullName -Raw | Should -Not -BeNullOrEmpty + } + } +} diff --git a/tests/Nix.Tests.ps1 b/tests/Nix.Tests.ps1 new file mode 100644 index 0000000..0387a5e --- /dev/null +++ b/tests/Nix.Tests.ps1 @@ -0,0 +1,62 @@ +param ( + [Version] $Version, + [String] $Platform +) + +Import-Module (Join-Path $PSScriptRoot "../helpers/pester-extensions.psm1") + +Set-Location "sources" +$env:Path="$env:Path;${env:BOOST_ROOT}\lib" + +if ($Platform -eq "linux-16.04") { + Write-Host "Install dependencies" + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 ` + --slave /usr/bin/g++ g++ /usr/bin/g++-7 + sudo update-alternatives --config gcc +} + +Describe "Nix Tests" { + + + It "Simple code" { + "g++ -w -I ${env:BOOST_ROOT}/include main.cpp -o headers_test" | Should -ReturnZeroExitCode + "./headers_test" | Should -ReturnZeroExitCode + } + + It "Test header existence" { + "g++ -w -std=c++14 -I ${env:BOOST_ROOT}/include main-headers.cpp -o headers_test_2" | Should -ReturnZeroExitCode + "./headers_test_2" | Should -ReturnZeroExitCode + } + + It "Test shared debug" { + $BuildParams = ( + "-w", "-DBOOST_LOG_DYN_LINK", + "-I", "${env:BOOST_ROOT}/include", + "-L", "${env:BOOST_ROOT}/lib", "main_log.cpp", + "-l:libboost_log_setup-mt-d-x64.so.${Version}", + "-l:libboost_log-mt-d-x64.so.${Version}", + "-l:libboost_thread-mt-d-x64.so.${Version}", + "-l:libboost_filesystem-mt-d-x64.so.${Version}", + "-lpthread" + ) + + "g++ $BuildParams -o test_shared_debug" | Should -ReturnZeroExitCode + "./test_shared_debug" | Should -ReturnZeroExitCode + } + + It "Test shared release" { + $BuildParams = ( + "-w", "-DBOOST_LOG_DYN_LINK", + "-I", "${env:BOOST_ROOT}/include", + "-L", "${env:BOOST_ROOT}/lib", "main_log.cpp", + "-l:libboost_log_setup-mt-x64.so.${Version}", + "-l:libboost_log-mt-x64.so.${Version}", + "-l:libboost_thread-mt-x64.so.${Version}", + "-l:libboost_filesystem-mt-x64.so.${Version}", + "-lpthread" + ) + + "g++ $BuildParams -o test_shared_release" | Should -ReturnZeroExitCode + "./test_shared_release" | Should -ReturnZeroExitCode + } +} \ No newline at end of file diff --git a/tests/Windows.Tests.ps1 b/tests/Windows.Tests.ps1 new file mode 100644 index 0000000..a821868 --- /dev/null +++ b/tests/Windows.Tests.ps1 @@ -0,0 +1,69 @@ +param ( + [Version] $Version, + [String] $Platform +) + +Import-Module (Join-Path $PSScriptRoot "../helpers/pester-extensions.psm1") +Import-Module (Join-Path $PSScriptRoot "../helpers/win-vs-env.psm1") + +Set-Location -Path "sources" + +$env:Path="$env:Path;${env:BOOST_ROOT}\lib" + +Write-Host "Initialize VS dev environment" +Invoke-VSDevEnvironment + +Describe "Windows Tests" { + It "Run simple code" { + "cl -nologo /EHsc -I ${env:BOOST_ROOT}\include main.cpp" | Should -ReturnZeroExitCode + ".\main.exe" | Should -ReturnZeroExitCode + } + + It "Build with static libraries 1" { + $buildArguments = @( + "/EHsc", + "/I", "${env:BOOST_ROOT}\include", + "main-headers.cpp", + "/link", "/LIBPATH:${env:BOOST_ROOT}\lib", + "/OUT:main_static_lib_1.exe" + ) + "cl -nologo $buildArguments" | Should -ReturnZeroExitCode + ".\main_static_lib_1.exe" | Should -ReturnZeroExitCode + } + + It "Build with dynamic libraries 1" { + $buildArguments = @( + "/EHsc", "/MD", + "/I", "${env:BOOST_ROOT}\include", + "main-headers.cpp", + "/link", "/LIBPATH:${env:BOOST_ROOT}\lib", + "/OUT:main_dynamic_lib_1.exe" + ) + "cl -nologo $buildArguments" | Should -ReturnZeroExitCode + ".\main_dynamic_lib_1.exe" | Should -ReturnZeroExitCode + } + + It "Build with static libraries 2" { + $buildArguments = @( + "/EHsc", + "/I", "${env:BOOST_ROOT}\include", + "main_log.cpp", + "/link", "/LIBPATH:${env:BOOST_ROOT}\lib", + "/OUT:main_static_lib_2.exe" + ) + "cl -nologo $buildArguments" | Should -ReturnZeroExitCode + ".\main_static_lib_2.exe" | Should -ReturnZeroExitCode + } + + It "Build with dynamic libraries 2" { + $buildArguments = @( + "/EHsc", "/MD", + "/I", "${env:BOOST_ROOT}\include", + "main_log.cpp", + "/link", "/LIBPATH:${env:BOOST_ROOT}\lib", + "/OUT:main_dynamic_lib_2.exe" + ) + "cl -nologo $buildArguments" | Should -ReturnZeroExitCode + ".\main_dynamic_lib_2.exe" | Should -ReturnZeroExitCode + } +} \ No newline at end of file diff --git a/tests/sources/main-headers.cpp b/tests/sources/main-headers.cpp new file mode 100644 index 0000000..39daa9f --- /dev/null +++ b/tests/sources/main-headers.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +#include + +int main() +{ + std::cout << "Using safe numerics" << std::endl; + try{ + using namespace boost::safe_numerics; + safe x = INT_MAX - 5; + ++x; + } + catch(const std::exception & e){ + std::cout << e.what() << std::endl; + std::cout << "error detected!" << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/tests/sources/main.cpp b/tests/sources/main.cpp new file mode 100644 index 0000000..eda7210 --- /dev/null +++ b/tests/sources/main.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +using namespace boost::algorithm; + +int main() +{ + std::string s = "Boost C++ Libraries"; + std::cout << to_upper_copy(s) << '\n'; + + return 0; +} \ No newline at end of file diff --git a/tests/sources/main_log.cpp b/tests/sources/main_log.cpp new file mode 100644 index 0000000..bb23bfd --- /dev/null +++ b/tests/sources/main_log.cpp @@ -0,0 +1,13 @@ +#include + +int main(int, char*[]) +{ + BOOST_LOG_TRIVIAL(trace) << "A trace severity message"; + BOOST_LOG_TRIVIAL(debug) << "A debug severity message"; + BOOST_LOG_TRIVIAL(info) << "An informational severity message"; + BOOST_LOG_TRIVIAL(warning) << "A warning severity message"; + BOOST_LOG_TRIVIAL(error) << "An error severity message"; + BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message"; + + return 0; +} \ No newline at end of file