public RuntimeGroup(ITaskItem item) { BaseRID = item.ItemSpec; Parent = item.GetString(nameof(Parent)); Versions = item.GetStrings(nameof(Versions)); TreatVersionsAsCompatible = item.GetBoolean(nameof(TreatVersionsAsCompatible), true); OmitVersionDelimiter = item.GetBoolean(nameof(OmitVersionDelimiter)); ApplyVersionsToParent = item.GetBoolean(nameof(ApplyVersionsToParent)); Architectures = item.GetStrings(nameof(Architectures)); AdditionalQualifiers = item.GetStrings(nameof(AdditionalQualifiers)); OmitRIDs = new HashSet <string>(item.GetStrings(nameof(OmitRIDs))); OmitRIDDefinitions = new HashSet <string>(item.GetStrings(nameof(OmitRIDDefinitions))); OmitRIDReferences = new HashSet <string>(item.GetStrings(nameof(OmitRIDReferences))); }
ITaskItem EnsurePackagePath(ITaskItem file, IDictionary <string, ITaskItem> kindMap) { // Switch to full path items var output = new TaskItem( file.GetBoolean("IsFile", true) && file.ItemSpec.IndexOfAny(Path.GetInvalidPathChars()) == -1 && File.Exists(file.GetMetadata("FullPath")) ? file.GetMetadata("FullPath") : file.ItemSpec); // Preserve existing metadata. file.CopyMetadataTo(output); // Map the pack folder to a target top-level directory. var packFolder = file.GetMetadata("PackFolder"); var packageFolder = ""; var frameworkSpecific = false; if (!string.IsNullOrEmpty(packFolder) && kindMap.TryGetValue(packFolder, out var kindItem)) { packageFolder = kindItem.GetMetadata(MetadataName.PackageFolder); bool.TryParse(kindItem.GetMetadata(MetadataName.FrameworkSpecific), out frameworkSpecific); } else if (!string.IsNullOrEmpty(packFolder)) { // By convention, we just turn the first letter of PackFolder to lowercase and assume that // to be a valid folder kind. packageFolder = char.IsLower(packFolder[0]) ? packFolder : char.ToLower(packFolder[0]).ToString() + packFolder.Substring(1); } // Specific PackageFile can always override PackFolder-inferred FrameworkSpecific value. if (bool.TryParse(file.GetMetadata(MetadataName.FrameworkSpecific), out var frameworkSpecificOverride)) { frameworkSpecific = frameworkSpecificOverride; } output.SetMetadata(MetadataName.PackageFolder, packageFolder.Replace('\\', '/')); // NOTE: a declared TargetFramework metadata trumps TargetFrameworkMoniker, // which is defaulted to that of the project being built. var targetFramework = output.GetMetadata(MetadataName.TargetFramework); if (string.IsNullOrEmpty(targetFramework) && frameworkSpecific) { var frameworkMoniker = file.GetTargetFrameworkMoniker(); targetFramework = frameworkMoniker.GetShortFrameworkName() ?? ""; // At this point we have the correct target framework output.SetMetadata(MetadataName.TargetFramework, targetFramework); } // If PackagePath already specified, we're done. if (file.TryGetMetadata("PackagePath", out var packagePath)) { // If PackagePath ends in directory separator, we assume // the file/path needs to be appended too. if (packagePath.EndsWith("\\") || packagePath.EndsWith("/")) { if (file.TryGetMetadata("Link", out var link)) { packagePath = Path.Combine(packagePath, link); } else if (Path.IsPathRooted(file.GetMetadata("RelativeDir"))) { // If the relative dir is absolute, we can just append the filename+extension instead packagePath = Path.Combine(packagePath, file.GetMetadata("FileName") + file.GetMetadata("Extension")); } else { packagePath = Path.Combine(packagePath, file.GetMetadata("RelativeDir"), file.GetMetadata("FileName") + file.GetMetadata("Extension")); } } // Always normalize our output output.SetMetadata("PackagePath", packagePath.Replace('\\', '/')); return(output); } // If a packaging project is requesting the package path assignment, // perform it regardless of whether there is a PackageId on the items, // since they all need to be assigned at this point. var isPackaging = false; isPackaging = !string.IsNullOrEmpty(IsPackaging) && bool.TryParse(IsPackaging, out isPackaging) && isPackaging; // If no PackageId specified, we let referencing projects define the package path // as it will be included in their package. // NOTE: if we don't do this, the package path of a project would be determined // by the declaring project's TFM, rather than the referencing one, and this // would be incorrect (i.e. a referenced PCL project that does not build a // nuget itself, would end up in the PCL lib folder rather than the referencing // package's lib folder for its own TFM, i.e. 'lib\net45'). if (string.IsNullOrEmpty(file.GetMetadata("PackageId")) && !isPackaging) { return(output); } // If we got this far but there wasn't a PackFolder to process, it's an error. if (string.IsNullOrEmpty(packFolder)) { Log.LogErrorCode(nameof(ErrorCode.NG0010), ErrorCode.NG0010(file.ItemSpec)); // We return the file anyway, since the task result will still be false. return(file); } // If the kind is known but it isn't mapped to a folder inside the package, we're done. // Special-case None kind since that means 'leave it wherever it lands' ;) if (string.IsNullOrEmpty(packageFolder) && !packFolder.Equals(PackFolderKind.None, StringComparison.OrdinalIgnoreCase)) { return(output); } // Special case for contentFiles, since they can also provide a codeLanguage metadata if (packageFolder == PackagingConstants.Folders.ContentFiles) { if (file.GetMetadata("TargetPath").StartsWith(packageFolder, StringComparison.OrdinalIgnoreCase)) { Log.LogErrorCode(nameof(ErrorCode.NG0013), ErrorCode.NG0013(file.GetMetadata("TargetPath"))); // We return the file anyway, since the task result will still be false. return(file); } // See https://docs.nuget.org/create/nuspec-reference#contentfiles-with-visual-studio-2015-update-1-and-later var codeLanguage = file.GetMetadata(MetadataName.ContentFile.CodeLanguage); if (string.IsNullOrEmpty(codeLanguage)) { codeLanguage = PackagingConstants.AnyCodeLanguage; output.SetMetadata(MetadataName.ContentFile.CodeLanguage, codeLanguage); } else { // This allows setting the codeLanguage to just the %(Extension) of the source file codeLanguage = codeLanguage.TrimStart('.'); } packageFolder = Path.Combine(packageFolder, codeLanguage); // And they also cannot have an empty framework, at most, it will be "any" if (string.IsNullOrEmpty(targetFramework)) { targetFramework = PackagingConstants.AnyFramework; } // Once TF is defaulted, a content file is actually always framework-specific, // although the framework may be 'any'. frameworkSpecific = true; // At this point we have the correct target framework for a content file, so persist it. output.SetMetadata(MetadataName.TargetFramework, targetFramework); } var targetPath = file.GetMetadata("TargetPath"); // Linked files already have the desired target path specified by the user if (string.IsNullOrEmpty(targetPath)) { targetPath = file.GetMetadata("Link"); } // NOTE: TargetPath allows a framework-specific file to still specify its relative // location without hardcoding the target framework (useful for multi-targetting and // P2P references). if (string.IsNullOrEmpty(targetPath)) { targetPath = string.IsNullOrEmpty(packageFolder) ? Path.Combine(file.GetMetadata("RelativeDir"), file.GetMetadata("FileName") + file.GetMetadata("Extension")) : // Well-known folders only get root-level files by default. Can be overriden with PackagePath or TargetPath // explicitly, of course file.GetMetadata("FileName") + file.GetMetadata("Extension"); } if (!string.IsNullOrEmpty(packageFolder) && targetPath.StartsWith(packageFolder + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { // Avoid duplicating already determined package folder in package path later on. targetPath = targetPath.Substring(packageFolder.Length + 1); } // At this point we have the correct calculated target path, so persist it for inspection if necessary. output.SetMetadata("TargetPath", targetPath.Replace('\\', '/')); // If we have no known package folder, files go to their RelativeDir location. // This allows custom packaging paths such as "workbooks", "docs" or whatever, which aren't prohibited by // the format. if (string.IsNullOrEmpty(packageFolder)) { // File goes to the determined target path (or the root of the package), such as a readme.txt packagePath = targetPath; } else { if (frameworkSpecific) { // For (framework-specific) tools, we need to append 'any' at the end if (packageFolder == PackagingConstants.Folders.Tools) { packagePath = Path.Combine(new[] { packageFolder, targetFramework, "any" }.Concat(targetPath.Split(Path.DirectorySeparatorChar)).ToArray()); } else { packagePath = Path.Combine(new[] { packageFolder, targetFramework }.Concat(targetPath.Split(Path.DirectorySeparatorChar)).ToArray()); } } else { packagePath = Path.Combine(new[] { packageFolder }.Concat(targetPath.Split(Path.DirectorySeparatorChar)).ToArray()); } } output.SetMetadata(MetadataName.PackagePath, packagePath.Replace('\\', '/')); return(output); }