private PackageData[] GetPackageDependencies(string targetPackagesPath, NuGetFramework targetFramework) { List <PackageData> dependencies = new List <PackageData>(); PackageDependencyGroup group = this.nReader.GetDependencyGroups().GetNearest(targetFramework); if (group != null) { foreach (var pkg in group.Packages) { var repository = new NuGetv3LocalRepository(targetPackagesPath); var dependencyPkgs = repository.FindPackagesById(pkg.Id); var allVersions = dependencyPkgs.Select(d => d.Version); NuGetVersion selectedVersion = pkg.VersionRange.FindBestMatch(allVersions); var selectedPkg = dependencyPkgs.FirstOrDefault(d => d.Version == selectedVersion); if (selectedPkg != null) { var pkgData = PackageData.GetPackageData(selectedPkg.Id, selectedPkg.Version.ToString()); if (pkgData != null) { dependencies.Add(pkgData); } } } } else { Console.WriteLine($"ERROR: Group not found for {this.SourceFileName} TFM {targetFramework}."); } return(dependencies.ToArray()); }
private ProjectData(string targetPackagesPath, string dllPath, string srcPath) { this.DllPath = dllPath; this.targetPackagesPath = targetPackagesPath; // Find the corresponding nuspec var splitPath = dllPath.Replace(targetPackagesPath, "").Split(Path.DirectorySeparatorChar); if (splitPath.Length < 4) { throw new ArgumentException("Path does not have expected depth", nameof(dllPath)); } var rootDir = Path.Combine(targetPackagesPath, Path.Combine(splitPath.Take(2).ToArray())); this.NuSpecFile = Directory.GetFiles(rootDir, "*.nuspec", SearchOption.TopDirectoryOnly)[0]; nReader = new NuspecReader(NuSpecFile); // Find the package name this.PackageName = nReader.GetId(); // Find the package version this.PackageVersion = nReader.GetVersion(); // Find the tfm for the dll this.ShortTfm = splitPath[splitPath.Length - 2]; // Find the project name for the dll this.ProjectFileName = dllPath.Replace(targetPackagesPath, srcPath).Replace(".dll", ".csproj"); // Find the source file name for the dll this.SourceFileName = dllPath.Replace(targetPackagesPath, srcPath).Replace(".dll", ".cs"); // Find the subPath for the dll (either ref or lib, usually) this.SubPath = splitPath[splitPath.Length - 3]; var pkgData = PackageData.GetOrCreatePackageData(srcPath, this.PackageName, this.PackageVersion.ToString()); pkgData.ReferenceProjects.Add(this); }
public override bool Execute() { DateTime startTime = DateTime.Now; string assemblyVersionTemplate = File.ReadAllText("AssemblyVersionTemplate.txt"); string pkgProjectTemplate = File.ReadAllText("PackageProjectTemplate.xml"); string[] standardUsings = File.ReadAllLines("AssemblyStandardUsings.txt"); Log.LogMessage(MessageImportance.Low, "Start generating csproj files."); // First, create project data for all passed in dlls foreach (ITaskItem dll in PackageDlls) { var pd = ProjectData.GetOrCreateProjectData(TargetPackagesPath, dll.GetMetadata("FullPath"), SrcPath); Log.LogMessage(MessageImportance.Low, $"Generating data for {pd.PackageName}/{pd.ShortTfm}"); } // Second, setup dependencies between all generated project data ProjectData.GenerateAllDependencies(); // Create a script file to copy the newly added project source to the output directory string projectSourceCopyCommands = PackageData.GetAll().Select(p => $"cp -r --parents ./{p.RelativePath}/* $1").Aggregate((a, b) => a + "\n" + b); projectSourceCopyCommands += "\nfind . -type f -iname Directory.Build.props -exec cp --parents {} $1 \\;"; File.WriteAllText(Path.Combine(SrcPath, "CopyProjects.sh"), projectSourceCopyCommands); foreach (var pkgData in PackageData.GetAll()) { string pkgProjectOutput = pkgProjectTemplate; string packageReferenceIncludes = "\n"; string outputPathByTfm = "\n"; bool includesNetstandard21 = pkgData.ReferenceProjects.Any(dep => dep.ShortTfm == "netstandard2.1"); bool includesNetcoreapp30 = pkgData.ReferenceProjects.Any(dep => dep.ShortTfm == "netcoreapp3.0"); foreach (var dep in pkgData.ReferenceProjects) { string packageReferences = ""; string netStandardTag = "NETStandardImplicitPackageVersion"; if (GeneratorVersion == 1) { netStandardTag = "NETStandardLibraryPackageVersion"; } if (dep.ShortTfm == "netstandard2.0" && !includesNetstandard21 && !includesNetcoreapp30) { packageReferences += $" <PackageReference Include=\"NETStandard.Library\" Version=\"$({netStandardTag})\" />\n"; } foreach (var pkgRef in dep.GetPackageReferences()) { string version = pkgRef.VersionRange.OriginalString; // TODO: Generate a lookup table from source-build/PackageVersions.props. For now, there is only one... if (pkgRef.Id == "NETStandard.Library") { if (!includesNetstandard21 && !includesNetcoreapp30) { packageReferences += $" <PackageReference Include=\"{pkgRef.Id}\" Version=\"$({netStandardTag})\" />\n"; } } else { packageReferences += $" <PackageReference Include=\"{pkgRef.Id}\" Version=\"{version}\" />\n"; } } if (dep.ShortTfm.StartsWith("net4")) { packageReferences += $" <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies.{dep.ShortTfm.Replace("net463","net462")}\" Version=\"1.0.2\" />\n"; } foreach (var fwkRef in dep.GetFrameworkReferences()) { if (fwkRef != "mscorlib") { packageReferences += $" <Reference Include=\"{fwkRef}\" />\n"; } } if (packageReferences != "") { packageReferenceIncludes += $" <ItemGroup Condition=\" '$(TargetFramework)' == '{dep.ShortTfm.Replace("net463","net462")}' \">\n"; packageReferenceIncludes += packageReferences; packageReferenceIncludes += $" </ItemGroup>\n\n"; } if (dep.SubPath == "lib") { outputPathByTfm += $" <PropertyGroup Condition=\" '$(TargetFramework)' == '{dep.ShortTfm.Replace("net463","net462")}' \">\n"; outputPathByTfm += $" <OutputPath>$(ArtifactsBinDir){pkgData.RelativePath}/{dep.SubPath}/</OutputPath>\n"; outputPathByTfm += $" </PropertyGroup>\n\n"; } } string targetFrameworks = pkgData.ReferenceProjects.Select(p => p.ShortTfm.Replace("net463", "net462")).Distinct().Aggregate((tfmSet, tfm) => tfmSet + ";" + tfm); string appStrongNameKeyFileName = GetStrongNameKeyFileName(pkgData.ReferenceProjects.First()); string keyFileTag = ""; if (GeneratorVersion == 1) { keyFileTag = $"\n <AssemblyOriginatorKeyFile>$(KeyFileDir){appStrongNameKeyFileName}.snk</AssemblyOriginatorKeyFile>"; } // Don't generate StrongNameKeyId for MSFT key for V2 Generator else if (appStrongNameKeyFileName != "MSFT") { string appStrongNameKeyId = GetStrongNameKeyId(pkgData.ReferenceProjects.First()); keyFileTag = $"\n <StrongNameKeyId>{appStrongNameKeyId}</StrongNameKeyId>"; } string enableImplicitReferencesTag = ""; if (includesNetstandard21 || includesNetcoreapp30) { enableImplicitReferencesTag = "\n <DisableImplicitFrameworkReferences>false</DisableImplicitFrameworkReferences>"; } pkgProjectOutput = pkgProjectOutput.Replace("#LowerCaseFileName#", pkgData.Id.ToLower()); pkgProjectOutput = pkgProjectOutput.Replace("#OutputPathByTfm#", outputPathByTfm); pkgProjectOutput = pkgProjectOutput.Replace("#RelativePath#", pkgData.RelativePath); pkgProjectOutput = pkgProjectOutput.Replace("#PackageReferences#", packageReferenceIncludes); pkgProjectOutput = pkgProjectOutput.Replace("#TargetFrameworks#", targetFrameworks); pkgProjectOutput = pkgProjectOutput.Replace("#KeyFileTag#", keyFileTag); pkgProjectOutput = pkgProjectOutput.Replace("#EnableImplicitReferencesTag#", enableImplicitReferencesTag); string projectOutputPath = pkgData.ProjectPath; if (GeneratorVersion == 2) { projectOutputPath = pkgData.V2ProjectPath; } Log.LogMessage(MessageImportance.High, $"Writing {projectOutputPath}"); Directory.CreateDirectory(Path.GetDirectoryName(projectOutputPath)); File.WriteAllText(projectOutputPath, pkgProjectOutput); // Write out Directory.Build.props for V2 if (GeneratorVersion == 2) { var directoryBuildPropsText = "<Project>\n" + "\n" + " <Import Project=\"$([MSBuild]::GetPathOfFileAbove(Directory.Build.props, $(MSBuildThisFileDirectory)..))\" />\n" + "\n" + " <PropertyGroup>\n" + $" <AssemblyName>{pkgData.Id}</AssemblyName>\n" + " </PropertyGroup>\n" + "\n" + "</Project>\n"; File.WriteAllText(pkgData.DirectoryBuildPropsPath, directoryBuildPropsText); } } // Last, generate the AssemblyVersion and csproj files for each dll, inserting the dependencies foreach (ProjectData pd in ProjectData.GetAllProjectData()) { string assemblyVersionData = assemblyVersionTemplate; string codeArtifactsPath = Path.GetDirectoryName(pd.ProjectFileName); string pacakageReferenceIncludes = ""; foreach (var dep in pd.Dependencies) { pacakageReferenceIncludes += $"<PackageReference Include=\"{dep.Id}\" Version=\"{dep.Version}\" />\n"; } List <string> usings = pd.GetAdditionalUsings(); usings.AddRange(standardUsings); var usingsText = usings.Distinct().OrderBy(u => u).Aggregate((a, b) => a + "\n" + b); assemblyVersionData = assemblyVersionData.Replace("#AssemblyUsings#", usingsText); assemblyVersionData = assemblyVersionData.Replace("#FileName#", pd.PackageName); assemblyVersionData = assemblyVersionData.Replace("#AssemblyVersion#", pd.GetAssemblyVersion()); assemblyVersionData = assemblyVersionData.Replace("#FileVersion#", pd.GetFileVersion()); assemblyVersionData = assemblyVersionData.Replace("#TypeForwards#", pd.GetTypeForwards()); // Some packages have a net463 TFM, which we can't work with. If both net462 and net463 is in a package, // just eliminate net463, otherwise, rename it to net462 string outputFilename = pd.SourceFileName; bool updateSrcText = true; if (pd.SourceFileName.Contains("net463")) { Log.LogMessage(MessageImportance.High, $"WARNING: Encountered package with net463 TFM: {pd.SourceFileName}"); outputFilename = pd.SourceFileName.Replace("net463", "net462"); if (File.Exists(outputFilename)) { File.Delete(pd.SourceFileName); updateSrcText = false; } else { Directory.CreateDirectory(Path.GetDirectoryName(outputFilename)); File.Move(pd.SourceFileName, outputFilename); } Directory.Delete(Path.GetDirectoryName(pd.SourceFileName)); } if (updateSrcText && File.Exists(outputFilename)) { Log.LogMessage(MessageImportance.Low, $"Updating {outputFilename} with assembly version attributes"); Directory.CreateDirectory(codeArtifactsPath); string srcText = File.ReadAllText(outputFilename); srcText = srcText.Replace("#AssemblyVersionAttributes#", assemblyVersionData); File.WriteAllText(outputFilename, srcText); } } Log.LogMessage(MessageImportance.High, $"Done generating csproj files. Took {DateTime.Now - startTime}"); return(true); }