src/Build-PackageData.ps1
function Build-PackageData { param( [Parameter(Mandatory)] $Bootstrapper, [Parameter(Mandatory)] [ValidateSet("Object","Install","File")] [string] $From, [Parameter(Mandatory)] $Options, $Manifest # Unused will have to be worked out at a later date <# The purpose of the manifest will be to modify how Install-Package handles Importing May need to account for skipping, special conditions, and possibly postload scripts There have been 3 considerations for where this manifest will be sourced from: - a .nupkg's .psd1 file - a .nupkg's .nuspec file - possibly a separate .json or .ps1 file #> ) $Out = @{ "Name" = "Undefined" "Version" = "Undefined" "Source" = "Undefined" "TempPath" = "Undefined" "Offline" = $false } $Options = If( $Options.Count -gt 1 ){ $temp_options = @{} $Options | ForEach-Object { $iter_options = $_ $Out.Keys | ForEach-Object { $temp_options[ $_ ] = $iter_options[ $_ ] } } $temp_options } Else { $Options } # For now, this option will be universal - this may or may not change $Out.TempPath = $Options.TempPath $Out.Offline = [bool] $Options.Offline switch( $From ){ "Object" { $out_keys = $Out.Keys | % { $_ } $out_keys | ForEach-Object { $Out[ $_ ] = $Options[ $_ ] } } "Install" { $package_attempts = @{} $package_attempts.local_latest = Get-Package $Options.Name -ProviderName NuGet -ErrorAction SilentlyContinue $version_available = Try{ If( $Options.Version ){ $Options.Version } Elseif( $Options.Offline ) { $package_attempts.local_latest.Version } Else { $Bootstrapper.GetLatest( $Options.Name ) } } Catch { $package_attempts.local_latest.Version } $install_conditions = @( (-not $package_attempts.local_latest), # Package not Installed ($package_attempts.local_latest.Version -ne $version_available ) # Package either not up to date, or isn't required version ) if( $install_conditions ){ $version_wanted = $version_available # For the purpose of self-documenting code $package_attempts.local_corrected_ver = Try { # Check if the wanted version exists in the old version cache Get-Package $Options.Name -RequiredVersion $version_wanted -ProviderName NuGet -ErrorAction Stop } Catch { # If it doesn't install it: Write-Verbose "[Import-Package:Preparation] Installing $( $Options.Name ) $version_wanted" Try { Install-Package $Options.Name ` -ProviderName NuGet ` -RequiredVersion $version_wanted ` -SkipDependencies ` -Force ` -ErrorAction Stop | Out-Null } Catch { Install-Package $Options.Name ` -ProviderName NuGet ` -RequiredVersion $version_wanted ` -SkipDependencies ` -Scope CurrentUser ` -Force | Out-Null } # Error check it and return it: Get-Package $Options.Name -RequiredVersion $version_wanted -ProviderName NuGet -ErrorAction Stop } } If( $package_attempts.local_corrected_ver ){ $Options.Version = $package_attempts.local_corrected_ver.Version $Options.Source = $package_attempts.local_corrected_ver.Source } Else { $Options.Version = $package_attempts.local_latest.Version $Options.Source = $package_attempts.local_latest.Source } $out_keys = $Out.Keys | % { $_ } $out_keys | ForEach-Object { $Out[ $_ ] = $Options[ $_ ] } } "File" { # This needs to be corrected by the .nuspec, if it is specified in the nuspec # Additionally, if the version is specifed in the .nuspec, it needs to be provided here $Out.Name = (Split-Path $Options.Source -Leaf) # Unpack the package to the TempPath temporary directory [System.IO.Compression.ZipFile]::ExtractToDirectory( $Options.Source.ToString(), $Options.TempPath.ToString() ) # Copy the nupkg to the temporary directory as well Copy-Item -Path $Options.Source.ToString() -Destination $Options.TempPath.ToString() -Force $Out.Source = Join-Path $Options.TempPath.ToString() (Split-Path $Options.Source -Leaf) } } $out_keys = $Out.Keys | % { $_ } $out_keys | ForEach-Object { If( $Out[ $_ ] -eq "Undefined" ){ $Out[ $_ ] = $Null } } <# If Manifest and skipping logic gets implemented, the skipping logic: - may occur further up in the function - should not occur after the .Source check below #> If( -not( Test-Path $Out.Source ) ){ Write-Error "[Import-Package:Preparation] Unable to find package $( $Out.Name )" return } Write-Verbose "[Import-Package:Preparation] Reading package $( $Out.Name )$( If( $Out.Version ) { " $( $Out.Version )"})" $Out.XML = $Bootstrapper.ReadNuspec( $Out.Source ) Write-Verbose "[Import-Package:Preparation] Validating .nuspec for $( $Out.Name )..." & { $nuspec_id = $Out.XML.package.metadata.id.ToString() $nuspec_version = $Out.XML.package.metadata.version.ToString() $versions_available = $nuspec_version -and $Out.Version $names_available = $nuspec_id -and $Out.Name $version_mismatch = $nuspec_version -ne $Out.Version $names_mismatch = $nuspec_id -ne $Out.Name If( $names_available -and $versions_available ){ If( $version_mismatch -and (-not $Unmanaged) ){ Throw "[Import-Package:Preparation] Version mismatch for $( $Out.Name )" return } Else { $Out.Version = $nuspec_version # For most cases these will already be equal, but for unmanaged it isn't } If( $names_mismatch ){ If( $Unmanaged ){ Write-Warning "[Import-Package:Preparation] Package $( $Out.Name ).nupkg has a nuspec with the name $nuspec_id. Changing name..." $Out.Name = $nuspec_id } Else { Throw "[Import-Package:Preparation] Package $( $Out.Name ).nupkg has a .nuspec with the invalid ID $nuspec_id." return } } } } Write-Verbose "[Import-Package:Preparation] Checking for OS-specific files" If( Test-Path "$(Split-Path $Out.Source )\runtimes" ){ $available_rids = Get-ChildItem "$(Split-Path $Out.Source )\runtimes" -Directory | Select-Object -ExpandProperty Name $Out.RIDs = $available_rids $rid = $Bootstrapper.graphs | ForEach-Object { <# A big problem with selecting a package RID is that there maybe: - multiple RID graphs - multiple RIDs on each graph - multiple RIDs in the nupkg (we will call these available_rids) The earlier an RID occurs on a RID graph, the more representative it is of the host operating system - for example, for a Windows 10 x64 graph: - "win10-x64" will typically be the first element - "win", "any", and "base" will be at the end of the graph To determine which available_rid is correct, we need to check how early on the graphs they are. To do that, we iterate over each graph, and record the index of each available_rid as they occur on said graph: #> $index_table = @{} $graph = $_ $available_rids | ForEach-Object { $i = $graph.IndexOf( $_ ) If( $i -ne -1 ){ # If they don't exist on the graph, we don't record them $index_table[ $_ ] = $i } } # for each available_rid that was found on the graph, # we return the one (as a key(rid)-value(index) pair) with the lowest index: $index_table.GetEnumerator() | Sort-Object -Property Value | Select-Object -First 1 # this will return null, if it wasn't found on the current graph } | Sort-Object -Property Value | Select-Object -First 1 # then we return the lowest kv-pair for all graphs If( Test-Path "$(Split-Path $Out.Source)\runtimes\$( $rid.Key )" ){ Write-Verbose "[Import-Package:Preparation] Found $( $Out.Name ) runtime files for this platform ($( $rid.Key ))." $Out.RID = $rid.Key Write-Verbose "[Import-Package:Preparation] Checking the OS-specific files for framework-specific files..." Try{ $rid_libs = Get-ChildItem "$(Split-Path $Out.Source )\runtimes\$( $rid.Key )\lib" -Directory -ErrorAction Stop | Select-Object -ExpandProperty Name $Out.RID_Frameworks = $rid_libs } Catch {} } } Write-Verbose "[Import-Package:Preparation] Checking OS-agnostic framework-specific files..." Try{ $Out.Frameworks = Get-ChildItem "$( Split-Path $Out.Source )\lib" -Directory -ErrorAction Stop | Select-Object -ExpandProperty Name } Catch {} Write-Verbose "[Import-Package:Preparation] Reading dependencies..." $out_dependencies = @{} Try { $out_dependencies.Agnostic = Resolve-DependencyVersions $Out.XML.package.metadata.dependencies.dependency } Catch {} Try{ $out_dependencies.ByFramework = & { $by_framework = @{} $Out.XML.package.metadata.dependencies.group | ForEach-Object { $group = $_ $by_framework[ $group.TargetFramework.ToString() ] = Resolve-DependencyVersions $group.dependency } $by_framework } } Catch {} If( $out_dependencies.Keys.Count ){ $Out.Dependencies = $out_dependencies } <# Output Object Keys: - TempPath - Offline - Name - Version - Source - XML # the nuspec as parsed XML - RIDs (if applicable) # Available RIDS in package - RID (if applicable) # The RID best-suiting the OS. This is set here, because it can't be selected by the user - RID_Frameworks (if applicable) # Any framework folders for the above RID - Frameworks # Platform agnostic framework folders - Dependencies - Agnostic - ByFramework #> $Out } |