Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<ProductDependencies>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19218.1">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19226.3">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>46718d98c0fd03690a6a8c83da692a4a85a17902</Sha>
<Sha>7bec23ce3da545d97f53f99abce457a2e252aa58</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>
184 changes: 184 additions & 0 deletions eng/common/SourceLinkValidation.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
param(
[Parameter(Mandatory=$true)][string] $InputPath, # Full path to directory where Symbols.NuGet packages to be checked are stored
[Parameter(Mandatory=$true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation
[Parameter(Mandatory=$true)][string] $SourceLinkToolPath, # Full path to directory where dotnet SourceLink CLI was installed
[Parameter(Mandatory=$true)][string] $GHRepoName, # GitHub name of the repo including the Org. E.g., dotnet/arcade
[Parameter(Mandatory=$true)][string] $GHCommit # GitHub commit SHA used to build the packages
)

# Cache/HashMap (File -> Exist flag) used to consult whether a file exist
# in the repository at a specific commit point. This is populated by inserting
# all files present in the repo at a specific commit point.
$global:RepoFiles = @{}

$ValidatePackage = {
param(
[string] $PackagePath # Full path to a Symbols.NuGet package
)

# Ensure input file exist
if (!(Test-Path $PackagePath)) {
throw "Input file does not exist: $PackagePath"
}

# Extensions for which we'll look for SourceLink information
# For now we'll only care about Portable & Embedded PDBs
$RelevantExtensions = @(".dll", ".exe", ".pdb")

Write-Host -NoNewLine "Validating" ([System.IO.Path]::GetFileName($PackagePath)) "... "

$PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath)
$ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageId
$FailedFiles = 0

Add-Type -AssemblyName System.IO.Compression.FileSystem

[System.IO.Directory]::CreateDirectory($ExtractPath);

$zip = [System.IO.Compression.ZipFile]::OpenRead($PackagePath)

$zip.Entries |
Where-Object {$RelevantExtensions -contains [System.IO.Path]::GetExtension($_.Name)} |
ForEach-Object {
$FileName = $_.FullName
$Extension = [System.IO.Path]::GetExtension($_.Name)
$FakeName = -Join((New-Guid), $Extension)
$TargetFile = Join-Path -Path $ExtractPath -ChildPath $FakeName

# We ignore resource DLLs
if ($FileName.EndsWith(".resources.dll")) {
return
}

[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile, $true)

$ValidateFile = {
param(
[string] $FullPath, # Full path to the module that has to be checked
[string] $RealPath,
[ref] $FailedFiles
)

# Makes easier to reference `sourcelink cli`
Push-Location $using:SourceLinkToolPath

$SourceLinkInfos = .\sourcelink.exe print-urls $FullPath | Out-String

if ($LASTEXITCODE -eq 0 -and -not ([string]::IsNullOrEmpty($SourceLinkInfos))) {
$NumFailedLinks = 0

# We only care about Http addresses
$Matches = (Select-String '(http[s]?)(:\/\/)([^\s,]+)' -Input $SourceLinkInfos -AllMatches).Matches

if ($Matches.Count -ne 0) {
$Matches.Value |
ForEach-Object {
$Link = $_
$CommitUrl = -Join("https://raw.githubusercontent.com/", $using:GHRepoName, "/", $using:GHCommit, "/")
$FilePath = $Link.Replace($CommitUrl, "")
$Status = 200
$Cache = $using:RepoFiles

if ( !($Cache.ContainsKey($FilePath)) ) {
try {
$Uri = $Link -as [System.URI]

# Only GitHub links are valid
if ($Uri.AbsoluteURI -ne $null -and $Uri.Host -match "github") {
$Status = (Invoke-WebRequest -Uri $Link -UseBasicParsing -Method HEAD -TimeoutSec 5).StatusCode
}
else {
$Status = 0
}
}
catch {
$Status = 0
}
}

if ($Status -ne 200) {
if ($NumFailedLinks -eq 0) {
if ($FailedFiles.Value -eq 0) {
Write-Host
}

Write-Host "`tFile $RealPath has broken links:"
}

Write-Host "`t`tFailed to retrieve $Link"

$NumFailedLinks++
}
}
}

if ($NumFailedLinks -ne 0) {
$FailedFiles.value++
$global:LASTEXITCODE = 1
}
}

Pop-Location
}

&$ValidateFile $TargetFile $FileName ([ref]$FailedFiles)
}

$zip.Dispose()

if ($FailedFiles -eq 0) {
Write-Host "Passed."
}
}

function ValidateSourceLinkLinks {
if (!($GHRepoName -Match "^[^\s\/]+/[^\s\/]+$")) {
Write-Host "GHRepoName should be in the format <org>/<repo>"
$global:LASTEXITCODE = 1
return
}

if (!($GHCommit -Match "^[0-9a-fA-F]{40}$")) {
Write-Host "GHCommit should be a 40 chars hexadecimal string"
$global:LASTEXITCODE = 1
return
}

$RepoTreeURL = -Join("https://api.github.com/repos/", $GHRepoName, "/git/trees/", $GHCommit, "?recursive=1")
$CodeExtensions = @(".cs", ".vb", ".fs", ".fsi", ".fsx", ".fsscript")

try {
# Retrieve the list of files in the repo at that particular commit point and store them in the RepoFiles hash
$Data = Invoke-WebRequest $RepoTreeURL | ConvertFrom-Json | Select-Object -ExpandProperty tree

foreach ($file in $Data) {
$Extension = [System.IO.Path]::GetExtension($file.path)

if ($CodeExtensions.Contains($Extension)) {
$RepoFiles[$file.path] = 1
}
}
}
catch {
Write-Host "Problems downloading the list of files from the repo. Url used: $RepoTreeURL"
$global:LASTEXITCODE = 1
return
}

if (Test-Path $ExtractPath) {
Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue
}

# Process each NuGet package in parallel
$Jobs = @()
Get-ChildItem "$InputPath\*.symbols.nupkg" |
ForEach-Object {
$Jobs += Start-Job -ScriptBlock $ValidatePackage -ArgumentList $_.FullName
}

foreach ($Job in $Jobs) {
Wait-Job -Id $Job.Id | Receive-Job
}
}

Measure-Command { ValidateSourceLinkLinks }
7 changes: 6 additions & 1 deletion eng/common/templates/job/job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ parameters:
# Optional: enable sending telemetry
enableTelemetry: false

# Optional: define the helix repo for telemeetry (example: 'dotnet/arcade')
# Optional: define the helix repo for telemetry (example: 'dotnet/arcade')
helixRepo: ''

# Optional: define the helix type for telemetry (example: 'build/product/')
helixType: ''

# Required: name of the job
name: ''

Expand Down Expand Up @@ -122,6 +125,8 @@ jobs:
displayName: 'Send Helix Start Telemetry'
inputs:
helixRepo: ${{ parameters.helixRepo }}
${{ if ne(parameters.helixType, '') }}:
helixType: ${{ parameters.helixType }}
buildConfig: $(_BuildConfig)
runAsPublic: ${{ parameters.runAsPublic }}
continueOnError: ${{ parameters.continueOnError }}
Expand Down
51 changes: 0 additions & 51 deletions eng/common/templates/steps/helix-publish.yml

This file was deleted.

2 changes: 1 addition & 1 deletion eng/common/templates/steps/send-to-helix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ parameters:
WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget."
IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set
Creator: '' # optional -- if the build is external, use this to specify who is sending the job
DisplayNamePrefix: 'Send job to Helix' # optional -- rename the beginning of the displayName of the steps in AzDO
DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO
condition: succeeded() # optional -- condition for step to execute; defaults to succeeded()
continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false

Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19218.1",
"Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19226.3",
"Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19069.2"
}
}
43 changes: 15 additions & 28 deletions src/fsharp/TypeRelations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ open FSharp.Compiler.Infos
open FSharp.Compiler.PrettyNaming

/// Implements a :> b without coercion based on finalized (no type variable) types
// QUERY: This relation is approximate and not part of the language specification.
// Note: This relation is approximate and not part of the language specification.
//
// Some appropriate uses:
// patcompile.fs: IsDiscrimSubsumedBy (approximate warning for redundancy of 'isinst' patterns)
Expand All @@ -25,37 +25,24 @@ open FSharp.Compiler.PrettyNaming
let rec TypeDefinitelySubsumesTypeNoCoercion ndeep g amap m ty1 ty2 =
if ndeep > 100 then error(InternalError("recursive class hierarchy (detected in TypeDefinitelySubsumesTypeNoCoercion), ty1 = " + (DebugPrint.showType ty1), m))
if ty1 === ty2 then true
// QUERY : quadratic
elif typeEquiv g ty1 ty2 then true
else
let ty1 = stripTyEqns g ty1
let ty2 = stripTyEqns g ty2
match ty1, ty2 with
| TType_app (tc1, l1), TType_app (tc2, l2) when tyconRefEq g tc1 tc2 ->
List.lengthsEqAndForall2 (typeEquiv g) l1 l2
| TType_ucase (tc1, l1), TType_ucase (tc2, l2) when g.unionCaseRefEq tc1 tc2 ->
List.lengthsEqAndForall2 (typeEquiv g) l1 l2
| TType_tuple (tupInfo1, l1), TType_tuple (tupInfo2, l2) ->
evalTupInfoIsStruct tupInfo1 = evalTupInfoIsStruct tupInfo2 &&
List.lengthsEqAndForall2 (typeEquiv g) l1 l2
| TType_fun (d1, r1), TType_fun (d2, r2) ->
typeEquiv g d1 d2 && typeEquiv g r1 r2
| TType_measure measure1, TType_measure measure2 ->
measureEquiv g measure1 measure2
| _ ->
(typeEquiv g ty1 g.obj_ty && isRefTy g ty2) || (* F# reference types are subtypes of type 'obj' *)
(isAppTy g ty2 &&
isRefTy g ty2 &&

((match GetSuperTypeOfType g amap m ty2 with
| None -> false
| Some ty -> TypeDefinitelySubsumesTypeNoCoercion (ndeep+1) g amap m ty1 ty) ||

(isInterfaceTy g ty1 &&
ty2 |> GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m
|> List.exists (TypeDefinitelySubsumesTypeNoCoercion (ndeep+1) g amap m ty1))))


// F# reference types are subtypes of type 'obj'
(typeEquiv g ty1 g.obj_ty && isRefTy g ty2) ||
// Follow the supertype chain
(isAppTy g ty2 &&
isRefTy g ty2 &&

((match GetSuperTypeOfType g amap m ty2 with
| None -> false
| Some ty -> TypeDefinitelySubsumesTypeNoCoercion (ndeep+1) g amap m ty1 ty) ||

// Follow the interface hierarchy
(isInterfaceTy g ty1 &&
ty2 |> GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m
|> List.exists (TypeDefinitelySubsumesTypeNoCoercion (ndeep+1) g amap m ty1))))

type CanCoerce = CanCoerce | NoCoerce

Expand Down