public static CommandLineApplication Create() { var app = new CommandLineApplication(throwOnUnexpectedArg: false) { Name = "dotnet ef", FullName = "Entity Framework .NET Core CLI Commands" }; // TODO better help output https://github.com/aspnet/EntityFramework/issues/5188 // app.HelpOption("-h|--help"); var targetProjectOption = app.Option( "-p|--project <PROJECT>", "The project to target (defaults to the project in the current directory). Can be a path to a project.json or a project directory."); var startupProjectOption = app.Option( "-s|--startup-project <PROJECT>", "The path to the project containing Startup (defaults to the target project). Can be a path to a project.json or a project directory."); var configurationOption = app.Option( "-c|--configuration <CONFIGURATION>", $"Configuration under which to load (defaults to {Constants.DefaultConfiguration})"); var frameworkOption = app.Option( "-f|--framework <FRAMEWORK>", $"Target framework to load from the startup project (defaults to the framework most compatible with {FrameworkConstants.CommonFrameworks.NetCoreApp10})."); var buildBasePathOption = app.Option( "-b|--build-base-path <OUTPUT_DIR>", "Directory in which to find temporary outputs."); var outputOption = app.Option( "-o|--output <OUTPUT_DIR>", "Directory in which to find outputs"); var noBuildOption = app.Option("--no-build", "Do not build before executing."); app.OnExecute(() => { var targetProjectPath = targetProjectOption.HasValue() ? targetProjectOption.Value() : Directory.GetCurrentDirectory(); Project targetProject; if (!ProjectReader.TryGetProject(targetProjectPath, out targetProject)) { throw new OperationException($"Could not load target project '{targetProjectPath}'"); } Reporter.Verbose.WriteLine(ToolsStrings.LogUsingTargetProject(targetProject.Name)); Project startupProject; if (startupProjectOption.HasValue()) { var startupPath = startupProjectOption.Value(); if (!ProjectReader.TryGetProject(startupPath, out startupProject)) { throw new OperationException($"Could not load project '{startupPath}'"); } } else { startupProject = targetProject; } Reporter.Verbose.WriteLine(ToolsStrings.LogUsingStartupProject(startupProject.Name)); var startupFramework = frameworkOption.HasValue() ? NuGetFramework.Parse(frameworkOption.Value()) : null; if (startupFramework == null) { var frameworks = startupProject.GetTargetFrameworks().Select(i => i.FrameworkName); startupFramework = NuGetFrameworkUtility.GetNearest(frameworks, FrameworkConstants.CommonFrameworks.NetCoreApp10, f => f) ?? frameworks.FirstOrDefault(); Reporter.Verbose.WriteLine(ToolsStrings.LogUsingFramework(startupFramework.GetShortFolderName())); } var configuration = configurationOption.Value(); if (configuration == null) { configuration = Constants.DefaultConfiguration; Reporter.Verbose.WriteLine(ToolsStrings.LogUsingConfiguration(configuration)); } if (!noBuildOption.HasValue()) { var buildExitCode = BuildCommandFactory.Create( startupProject.ProjectFilePath, configuration, startupFramework, buildBasePathOption.Value(), outputOption.Value()) .ForwardStdErr() .ForwardStdOut() .Execute() .ExitCode; if (buildExitCode != 0) { throw new OperationException(ToolsStrings.BuildFailed(startupProject.Name)); } } var startupProjectContext = ProjectContext.Create( startupProject.ProjectFilePath, startupFramework, RuntimeEnvironmentRidExtensions.GetAllCandidateRuntimeIdentifiers()); var startupOutputPaths = startupProjectContext .GetOutputPaths(configuration, buildBasePathOption.Value(), outputOption.Value()); // TODO remove when https://github.com/dotnet/cli/issues/2645 is resolved Func <bool> isClassLibrary = () => { return(startupOutputPaths.RuntimeFiles == null || ( startupFramework.IsDesktop() ? !Directory.Exists(startupOutputPaths.RuntimeFiles.BasePath) : !File.Exists(startupOutputPaths.RuntimeFiles.RuntimeConfigJson) || !File.Exists(startupOutputPaths.RuntimeFiles.DepsJson) )); }; Reporter.Verbose.WriteLine(ToolsStrings.LogDataDirectory(startupOutputPaths.RuntimeOutputPath)); // Workaround https://github.com/dotnet/cli/issues/3164 var isExecutable = startupProject.GetCompilerOptions(startupFramework, configuration).EmitEntryPoint ?? startupProject.GetCompilerOptions(null, configuration).EmitEntryPoint.GetValueOrDefault(); var startupAssembly = isExecutable ? startupOutputPaths.RuntimeFiles.Executable : startupOutputPaths.RuntimeFiles.Assembly; var targetAssembly = targetProject.ProjectFilePath.Equals(startupProject.ProjectFilePath) ? startupAssembly // This assumes the target assembly is present in the startup project context and is a *.dll // TODO create a project context for target project as well to ensure filename is correct : Path.Combine(startupOutputPaths.RuntimeOutputPath, targetProject.GetCompilerOptions(null, configuration).OutputName + FileNameSuffixes.DotNet.DynamicLib); Reporter.Verbose.WriteLine(ToolsStrings.LogBeginDispatch(ProjectDependencyToolName, startupProject.Name)); try { bool isVerbose; bool.TryParse(Environment.GetEnvironmentVariable(CommandContext.Variables.Verbose), out isVerbose); var dispatchArgs = CreateArgs( assembly: targetAssembly, startupAssembly: startupOutputPaths.RuntimeFiles.Assembly, dispatcherVersion: ThisAssemblyVersion, dataDir: startupOutputPaths.RuntimeOutputPath, contentRootPath: startupProject.ProjectDirectory, projectDir: targetProject.ProjectDirectory, rootNamespace: targetProject.Name, verbose: isVerbose) .Concat(app.RemainingArguments); var buildBasePath = buildBasePathOption.Value(); if (buildBasePath != null && !Path.IsPathRooted(buildBasePath)) { // ProjectDependenciesCommandFactory cannot handle relative build base paths. buildBasePath = Path.Combine(Directory.GetCurrentDirectory(), buildBasePath); } return(new ProjectDependenciesCommandFactory( startupFramework, configuration, outputOption.Value(), buildBasePath, startupProject.ProjectDirectory) .Create(ProjectDependencyToolName, dispatchArgs, startupFramework, configuration) .ForwardStdErr() .ForwardStdOut() .Execute() .ExitCode); } catch (CommandUnknownException ex) { Reporter.Verbose.WriteLine(ex.Message); var fwlink = "http://go.microsoft.com/fwlink/?LinkId=798221"; if (isClassLibrary()) { Reporter.Error.WriteLine( ToolsStrings.ClassLibrariesNotSupportedInCli(startupProject.Name, fwlink).Bold().Red()); } else if (startupFramework.IsDesktop() && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Reporter.Error.WriteLine( ToolsStrings.DesktopCommandsRequiresWindows(startupFramework.GetShortFolderName()).Bold().Red()); } else { Reporter.Error.WriteLine( ToolsStrings.ProjectDependencyCommandNotFound( startupProject.Name, ProjectDependencyToolName, DispatcherToolName, fwlink).Bold().Red()); } return(1); } }); return(app); }
private static IEnumerable <Package> GetUpdates( IEnumerable <Package> packages, ILookup <string, Tuple <NuGetVersion, VersionRange> > versionLookup, IEnumerable <NuGetFramework> targetFrameworkValues, bool includeAllVersions, string semVerLevel) { var isSemVerLevelCompliant = SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel).Compile(); var updates = from p in packages.AsEnumerable() let version = NuGetVersion.Parse(p.Version) where isSemVerLevelCompliant(p) && versionLookup[p.PackageRegistration.Id].Any(versionTuple => { NuGetVersion clientVersion = versionTuple.Item1; var supportedPackageFrameworks = p.SupportedFrameworks.Select(f => f.FrameworkName); VersionRange versionConstraint = versionTuple.Item2; return(version > clientVersion && (targetFrameworkValues == null || !supportedPackageFrameworks.Any() || targetFrameworkValues.Any(s => supportedPackageFrameworks.Any(supported => NuGetFrameworkUtility.IsCompatibleWithFallbackCheck(s, supported)))) && (versionConstraint == null || versionConstraint.Satisfies(version))); }) select p; if (!includeAllVersions) { updates = updates.GroupBy(p => p.PackageRegistration.Id) .Select(g => g.OrderByDescending(p => NuGetVersion.Parse(p.Version)).First()); } return(updates); }
/// <summary> /// The lock file will get invalidated if one or more of the below are true /// 1. The target frameworks list of the current project was updated. /// 2. The runtime list of the current project waw updated. /// 3. The packages of the current project were updated. /// 4. The packages of the dependent projects were updated. /// 5. The framework list of the dependent projects were updated with frameworks incompatible with the main project framework. /// 6. If the version of the <paramref name="nuGetLockFile"/> is larger than the current tools <see cref="PackagesLockFileFormat.PackagesLockFileVersion"/>. /// </summary> /// <param name="dgSpec">The <see cref="DependencyGraphSpec"/> for the new project defintion.</param> /// <param name="nuGetLockFile">The current <see cref="PackagesLockFile"/>.</param> /// <returns>Returns LockFileValidityWithInvalidReasons object with IsValid set to true if the lock file is valid false otherwise. /// The second return type is a localized message that indicates in further detail the reason for the inconsistency.</returns> public static LockFileValidationResult IsLockFileValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { if (dgSpec == null) { throw new ArgumentNullException(nameof(dgSpec)); } if (nuGetLockFile == null) { throw new ArgumentNullException(nameof(nuGetLockFile)); } List <string> invalidReasons = new List <string>(); // Current tools know how to read only previous formats including the current if (PackagesLockFileFormat.PackagesLockFileVersion < nuGetLockFile.Version) { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_IncompatibleLockFileVersion, PackagesLockFileFormat.PackagesLockFileVersion )); return(new LockFileValidationResult(false, invalidReasons)); } var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies NuGetFramework[] lockFileFrameworks = nuGetLockFile.Targets .Where(t => t.TargetFramework != null) .Select(t => t.TargetFramework) .Distinct() .ToArray(); if (project.TargetFrameworks.Count != lockFileFrameworks.Length) { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_MismatchedTargetFrameworks, string.Join(",", project.TargetFrameworks.Select(e => e.FrameworkName.GetShortFolderName())), string.Join(",", lockFileFrameworks.Select(e => e.GetShortFolderName())) )); } else { // Validate the runtimes for the current project did not change. var projectRuntimesKeys = project.RuntimeGraph.Runtimes.Select(r => r.Key).Where(k => k != null); var lockFileRuntimes = nuGetLockFile.Targets.Select(t => t.RuntimeIdentifier).Where(r => r != null).Distinct(); if (!projectRuntimesKeys.OrderedEquals( lockFileRuntimes, x => x, StringComparer.InvariantCultureIgnoreCase, StringComparer.InvariantCultureIgnoreCase)) { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_RuntimeIdentifiersChanged, string.Join(";", projectRuntimesKeys.OrderBy(e => e)), string.Join(";", lockFileRuntimes.OrderBy(e => e)) )); } foreach (var framework in project.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_NewTargetFramework, framework.FrameworkName.GetShortFolderName()) ); continue; } IEnumerable <LockFileDependency> directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); (var hasProjectDependencyChanged, var pmessage) = HasDirectPackageDependencyChanged(framework.Dependencies, directDependencies, target.TargetFramework); if (hasProjectDependencyChanged) { // lock file is out of sync invalidReasons.Add(pmessage); } var transitiveDependenciesEnforcedByCentralVersions = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.CentralTransitive).ToList(); var transitiveDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Transitive).ToList(); (var hasTransitiveDependencyChanged, var tmessage) = HasProjectTransitiveDependencyChanged(framework.CentralPackageVersions, transitiveDependenciesEnforcedByCentralVersions, transitiveDependencies); if (hasTransitiveDependencyChanged) { // lock file is out of sync invalidReasons.Add(tmessage); } } // Validate all P2P references foreach (var restoreMetadataFramework in project.RestoreMetadata.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, restoreMetadataFramework.FrameworkName)); if (target == null) { continue; } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in restoreMetadataFramework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { PackageSpec spec = dgSpec.GetProjectSpec(projectReference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(spec.Name, projectReference.ProjectUniqueName)); while (queue.Count > 0) { var projectNames = queue.Dequeue(); var p2pUniqueName = projectNames.Item2; var p2pProjectName = projectNames.Item1; var projectDependency = target.Dependencies.FirstOrDefault( dep => dep.Type == PackageDependencyType.Project && StringComparer.OrdinalIgnoreCase.Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // new direct project dependency. // If there are changes in the P2P2P references, they will be caught in HasP2PDependencyChanged. invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_ProjectReferenceAdded, p2pProjectName, target.TargetFramework.GetShortFolderName() )); continue; } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); if (p2pSpec != null) { TargetFrameworkInformation p2pSpecTargetFrameworkInformation = default; if (p2pSpec.RestoreMetadata.ProjectStyle == ProjectStyle.PackagesConfig || p2pSpec.RestoreMetadata.ProjectStyle == ProjectStyle.Unknown) { // Skip compat check and dependency check for non PR projects. // Projects that are not PR do not undergo compat checks by NuGet and do not contribute anything transitively. p2pSpecTargetFrameworkInformation = p2pSpec.TargetFrameworks.FirstOrDefault(); } else { // This does not consider ATF. p2pSpecTargetFrameworkInformation = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, restoreMetadataFramework.FrameworkName, e => e.FrameworkName); } // No compatible framework found if (p2pSpecTargetFrameworkInformation != null) { // We need to compare the main framework only. Ignoring fallbacks. var p2pSpecProjectRestoreMetadataFrameworkInfo = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => NuGetFramework.Comparer.Equals(p2pSpecTargetFrameworkInformation.FrameworkName, t.FrameworkName)); if (p2pSpecProjectRestoreMetadataFrameworkInfo != null) { (var hasChanged, var message) = HasP2PDependencyChanged(p2pSpecTargetFrameworkInformation.Dependencies, p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences, projectDependency, dgSpec); if (hasChanged) { // P2P transitive package dependencies have changed invalidReasons.Add(message); } foreach (var reference in p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences) { // Do not add private assets for processing. if (visitedP2PReference.Add(reference.ProjectUniqueName) && reference.PrivateAssets != LibraryIncludeFlags.All) { var referenceSpec = dgSpec.GetProjectSpec(reference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(referenceSpec.Name, reference.ProjectUniqueName)); } } } else // This should never happen. { throw new Exception(string.Format(CultureInfo.CurrentCulture, Strings.PackagesLockFile_RestoreMetadataMissingTfms)); } } else { invalidReasons.Add(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_ProjectReferenceHasNoCompatibleTargetFramework, p2pProjectName, restoreMetadataFramework.FrameworkName.GetShortFolderName() )); } } else // This can't happen. When adding the queue, the referenceSpec HAS to be discovered. If the project is otherwise missing, it will be discovered in HasP2PDependencyChanged { throw new Exception(string.Format( CultureInfo.CurrentCulture, Strings.PackagesLockFile_UnableToLoadPackagespec, p2pUniqueName)); } } } } } } bool isLockFileValid = invalidReasons.Count == 0; return(new LockFileValidationResult(isLockFileValid, invalidReasons)); }
/// <summary> /// Asynchronously gets reference items. /// </summary> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>A task that represents the asynchronous operation. /// The task result (<see cref="Task{TResult}.Result" />) returns an /// <see cref="IEnumerable{FrameworkSpecificGroup}" />.</returns> /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" /> /// is cancelled.</exception> public override async Task <IEnumerable <FrameworkSpecificGroup> > GetReferenceItemsAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var nuspecReader = await GetNuspecReaderAsync(cancellationToken).ConfigureAwait(false); var referenceGroups = nuspecReader.GetReferenceGroups(); var fileGroups = new List <FrameworkSpecificGroup>(); // filter out non reference assemblies foreach (var group in await GetLibItemsAsync(cancellationToken).ConfigureAwait(false)) { fileGroups.Add(new FrameworkSpecificGroup(group.TargetFramework, group.Items.Where(e => IsReferenceAssembly(e)))); } // results var libItems = new List <FrameworkSpecificGroup>(); if (referenceGroups.Any()) { // the 'any' group from references, for pre2.5 nuspecs this will be the only group var fallbackGroup = referenceGroups.Where(g => g.TargetFramework.Equals(NuGetFramework.AnyFramework)).FirstOrDefault(); foreach (var fileGroup in fileGroups) { // check for a matching reference group to use for filtering var referenceGroup = NuGetFrameworkUtility.GetNearest( items: referenceGroups, framework: fileGroup.TargetFramework, frameworkMappings: FrameworkProvider, compatibilityProvider: CompatibilityProvider); if (referenceGroup == null) { referenceGroup = fallbackGroup; } if (referenceGroup == null) { // add the lib items without any filtering libItems.Add(fileGroup); } else { var filteredItems = new List <string>(); foreach (var path in fileGroup.Items) { // reference groups only have the file name, not the path var file = Path.GetFileName(path); if (referenceGroup.Items.Any(s => StringComparer.OrdinalIgnoreCase.Equals(s, file))) { filteredItems.Add(path); } } if (filteredItems.Any()) { libItems.Add(new FrameworkSpecificGroup(fileGroup.TargetFramework, filteredItems)); } } } } else { libItems.AddRange(fileGroups); } return(libItems); }
internal static T GetNearest <T>(IEnumerable <T> items) where T : IFrameworkSpecific { return(NuGetFrameworkUtility.GetNearest(items, NuGetFramework.Parse(_targetFramework))); }
internal static IFrameworkSpecific GetNearest(IEnumerable <string> items) { return(NuGetFrameworkUtility.GetNearest(items.Select(item => (IFrameworkSpecific)NuGetFramework.Parse(item)), NuGetFramework.Parse(_targetFramework))); }
private ProjectContext SelectCompatibleFramework(NuGetFramework target, IEnumerable <ProjectContext> contexts) { return(NuGetFrameworkUtility.GetNearest(contexts, target ?? FrameworkConstants.CommonFrameworks.NetCoreApp10, f => f.TargetFramework) ?? contexts.First()); }
private ITaskItem AssignNearestFrameworkForSingleReference( ITaskItem project, NuGetFramework projectNuGetFramework, IList <NuGetFramework> fallbackNuGetFrameworks, MSBuildLogger logger) { var itemWithProperties = new TaskItem(project); var referencedProjectFrameworkString = project.GetMetadata(TARGET_FRAMEWORKS); var referenceTargetFrameworkMonikers = project.GetMetadata(TARGET_FRAMEWORK_MONIKERS); var referencedProjectPlatformString = project.GetMetadata(TARGET_PLATFORM_MONIKERS); var referencedProjectFile = project.GetMetadata(MSBUILD_SOURCE_PROJECT_FILE); if (string.IsNullOrEmpty(referencedProjectFrameworkString)) { // No target frameworks set, nothing to do. return(itemWithProperties); } var referencedProjectFrameworks = MSBuildStringUtility.Split(referencedProjectFrameworkString); var referencedProjectTargetFrameworkMonikers = MSBuildStringUtility.Split(referenceTargetFrameworkMonikers); var referencedProjectTargetPlatformMonikers = MSBuildStringUtility.Split(referencedProjectPlatformString); if (referencedProjectTargetFrameworkMonikers.Length > 0 && (referencedProjectTargetFrameworkMonikers.Length != referencedProjectTargetPlatformMonikers.Length || referencedProjectTargetFrameworkMonikers.Length != referencedProjectFrameworks.Length)) { logger.LogError($"Internal error for {CurrentProjectName}." + $" Expected {TARGET_FRAMEWORKS}:{referencedProjectFrameworks}, " + $"{TARGET_FRAMEWORK_MONIKERS}:{referenceTargetFrameworkMonikers}, " + $"{TARGET_PLATFORM_MONIKERS}:{referencedProjectPlatformString} to have the same number of elements."); return(itemWithProperties); } // TargetFrameworks, TargetFrameworkMoniker, TargetPlatforMoniker var targetFrameworkInformations = new List <TargetFrameworkInformation>(); var useTargetMonikers = referencedProjectTargetFrameworkMonikers.Length > 0; for (int i = 0; i < referencedProjectFrameworks.Length; i++) { targetFrameworkInformations.Add(new TargetFrameworkInformation( referencedProjectFrameworks[i], useTargetMonikers ? referencedProjectTargetFrameworkMonikers[i] : null, useTargetMonikers ? referencedProjectTargetPlatformMonikers[i] : null)); } // try project framework var nearestNuGetFramework = NuGetFrameworkUtility.GetNearest(targetFrameworkInformations, projectNuGetFramework, GetNuGetFramework); if (nearestNuGetFramework != null) { itemWithProperties.SetMetadata(NEAREST_TARGET_FRAMEWORK, nearestNuGetFramework._targetFrameworkAlias); return(itemWithProperties); } // try project fallback frameworks foreach (var currentProjectTargetFramework in fallbackNuGetFrameworks) { nearestNuGetFramework = NuGetFrameworkUtility.GetNearest(targetFrameworkInformations, currentProjectTargetFramework, GetNuGetFramework); if (nearestNuGetFramework != null) { var message = string.Format(CultureInfo.CurrentCulture, Strings.ImportsFallbackWarning, referencedProjectFile, currentProjectTargetFramework.DotNetFrameworkName, projectNuGetFramework.DotNetFrameworkName); var warning = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1702, message); warning.LibraryId = referencedProjectFile; warning.ProjectPath = CurrentProjectName; // log NU1702 for ATF on project reference logger.Log(warning); itemWithProperties.SetMetadata(NEAREST_TARGET_FRAMEWORK, nearestNuGetFramework._targetFrameworkAlias); return(itemWithProperties); } } // no match found logger.LogError(string.Format(CultureInfo.CurrentCulture, Strings.NoCompatibleTargetFramework, project.ItemSpec, projectNuGetFramework.DotNetFrameworkName, referencedProjectFrameworkString)); return(itemWithProperties); }
public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies foreach (var framework in project.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } } // Validate all P2P references foreach (var framework in project.RestoreMetadata.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. return(false); } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { var spec = dgSpec.GetProjectSpec(projectReference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(spec.Name, projectReference.ProjectUniqueName)); while (queue.Count > 0) { var projectNames = queue.Dequeue(); var p2pUniqueName = projectNames.Item2; var p2pProjectName = projectNames.Item1; var projectDependency = target.Dependencies.FirstOrDefault( dep => dep.Type == PackageDependencyType.Project && PathUtility.GetStringComparerBasedOnOS().Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // The package spec not found in the dg spec. This could mean that the project does not exist anymore. if (p2pSpec != null) { var p2pSpecTarget = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, framework.FrameworkName, e => e.FrameworkName); // No compatible framework found if (p2pSpecTarget != null) { var p2pSpecProjectRefTarget = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(p2pSpecTarget.FrameworkName, t.FrameworkName)); if (p2pSpecProjectRefTarget != null) // This should never happen. { if (HasP2PDependencyChanged(p2pSpecTarget.Dependencies, p2pSpecProjectRefTarget.ProjectReferences, projectDependency, dgSpec)) { // P2P transitive package dependencies have changed return(false); } foreach (var reference in p2pSpecProjectRefTarget.ProjectReferences) { if (visitedP2PReference.Add(reference.ProjectUniqueName)) { var referenceSpec = dgSpec.GetProjectSpec(reference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(referenceSpec.Name, reference.ProjectUniqueName)); } } } else { return(false); } } else { return(false); } } else { return(false); } } } } } return(true); }
/// <summary> /// The lock file will get invalidated if one or more of the below are true /// 1. The target frameworks list of the current project was updated. /// 2. The runtime list of the current project waw updated. /// 3. The packages of the current project were updated. /// 4. The packages of the dependent projects were updated. /// 5. The framework list of the dependent projects were updated with frameworks incompatible with the main project framework. /// 6. If the version of the <paramref name="nuGetLockFile"/> is larger than the current tools <see cref="PackagesLockFileFormat.PackagesLockFileVersion"/>. /// </summary> /// <param name="dgSpec">The <see cref="DependencyGraphSpec"/> for the new project defintion.</param> /// <param name="nuGetLockFile">The current <see cref="PackagesLockFile"/>.</param> /// <returns>True if the lock file is valid false otherwise. </returns> public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { // Current tools know how to read only previous formats including the current if (PackagesLockFileFormat.PackagesLockFileVersion < nuGetLockFile.Version) { return(false); } var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies var lockFileFrameworks = nuGetLockFile.Targets .Where(t => t.TargetFramework != null) .Select(t => t.TargetFramework) .Distinct(); if (project.TargetFrameworks.Count != lockFileFrameworks.Count()) { return(false); } foreach (var framework in project.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } var transitiveDependenciesEnforcedByCentralVersions = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.CentralTransitive).ToList(); var transitiveDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Transitive).ToList(); if (HasProjectTransitiveDependencyChanged(framework.CentralPackageVersions, transitiveDependenciesEnforcedByCentralVersions, transitiveDependencies)) { // lock file is out of sync return(false); } } // Validate the runtimes for the current project did not change. var projectRuntimesKeys = project.RuntimeGraph.Runtimes.Select(r => r.Key).Where(k => k != null); var lockFileRuntimes = nuGetLockFile.Targets.Select(t => t.RuntimeIdentifier).Where(r => r != null).Distinct(); if (!projectRuntimesKeys.OrderedEquals( lockFileRuntimes, x => x, StringComparer.InvariantCultureIgnoreCase, StringComparer.InvariantCultureIgnoreCase)) { return(false); } // Validate all P2P references foreach (var framework in project.RestoreMetadata.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. return(false); } var queue = new Queue <Tuple <string, string> >(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { var spec = dgSpec.GetProjectSpec(projectReference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(spec.Name, projectReference.ProjectUniqueName)); while (queue.Count > 0) { var projectNames = queue.Dequeue(); var p2pUniqueName = projectNames.Item2; var p2pProjectName = projectNames.Item1; var projectDependency = target.Dependencies.FirstOrDefault( dep => dep.Type == PackageDependencyType.Project && StringComparer.OrdinalIgnoreCase.Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // The package spec not found in the dg spec. This could mean that the project does not exist anymore. if (p2pSpec != null) { // This does not consider ATF. var p2pSpecTargetFrameworkInformation = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, framework.FrameworkName, e => e.FrameworkName); // No compatible framework found if (p2pSpecTargetFrameworkInformation != null) { // We need to compare the main framework only. Ignoring fallbacks. var p2pSpecProjectRestoreMetadataFrameworkInfo = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => NuGetFramework.Comparer.Equals(p2pSpecTargetFrameworkInformation.FrameworkName, t.FrameworkName)); if (p2pSpecProjectRestoreMetadataFrameworkInfo != null) { if (HasP2PDependencyChanged(p2pSpecTargetFrameworkInformation.Dependencies, p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences, projectDependency, dgSpec)) { // P2P transitive package dependencies have changed return(false); } foreach (var reference in p2pSpecProjectRestoreMetadataFrameworkInfo.ProjectReferences) { // Do not add private assets for processing. if (visitedP2PReference.Add(reference.ProjectUniqueName) && reference.PrivateAssets != LibraryIncludeFlags.All) { var referenceSpec = dgSpec.GetProjectSpec(reference.ProjectUniqueName); queue.Enqueue(new Tuple <string, string>(referenceSpec.Name, reference.ProjectUniqueName)); } } } else // This should never happen. { return(false); } } else { return(false); } } else { return(false); } } } } } return(true); }
private MSBuildRestoreResult RestoreMSBuildFiles(PackageSpec project, IEnumerable <RestoreTargetGraph> targetGraphs, IReadOnlyList <NuGetv3LocalRepository> repositories, RemoteWalkContext context) { // Get the project graph var projectFrameworks = project.TargetFrameworks.Select(f => f.FrameworkName).ToList(); // Non-Msbuild projects should skip targets and treat it as success if (!context.IsMsBuildBased) { return(new MSBuildRestoreResult(project.Name, project.BaseDirectory, success: true)); } // Invalid msbuild projects should write out an msbuild error target if (projectFrameworks.Count != 1 || !targetGraphs.Any()) { return(new MSBuildRestoreResult(project.Name, project.BaseDirectory, success: false)); } // Gather props and targets to write out var graph = targetGraphs .Single(g => g.Framework.Equals(projectFrameworks[0]) && string.IsNullOrEmpty(g.RuntimeIdentifier)); var flattenedFlags = IncludeFlagUtils.FlattenDependencyTypes(_includeFlagGraphs, _request.Project, graph); var targets = new List <string>(); var props = new List <string>(); foreach (var library in graph.Flattened .Distinct() .OrderBy(g => g.Data.Match.Library)) { var includeLibrary = true; LibraryIncludeFlags libraryFlags; if (flattenedFlags.TryGetValue(library.Key.Name, out libraryFlags)) { includeLibrary = libraryFlags.HasFlag(LibraryIncludeFlags.Build); } // Skip libraries that do not include build files such as transitive packages if (includeLibrary) { var packageIdentity = new PackageIdentity(library.Key.Name, library.Key.Version); IList <string> packageFiles; context.PackageFileCache.TryGetValue(packageIdentity, out packageFiles); if (packageFiles != null) { var contentItemCollection = new ContentItemCollection(); contentItemCollection.Load(packageFiles); // Find MSBuild thingies var groups = contentItemCollection.FindItemGroups(graph.Conventions.Patterns.MSBuildFiles); // Find the nearest msbuild group, this can include the root level Any group. var buildItems = NuGetFrameworkUtility.GetNearest( groups, graph.Framework, group => group.Properties[ManagedCodeConventions.PropertyNames.TargetFrameworkMoniker] as NuGetFramework); if (buildItems != null) { // We need to additionally filter to items that are named "{packageId}.targets" and "{packageId}.props" // Filter by file name here and we'll filter by extension when we add things to the lists. var items = buildItems.Items .Where(item => Path.GetFileNameWithoutExtension(item.Path) .Equals(library.Key.Name, StringComparison.OrdinalIgnoreCase)) .ToList(); var packageInfo = NuGetv3LocalRepositoryUtility.GetPackage(repositories, library.Key.Name, library.Key.Version); var pathResolver = packageInfo.Repository.PathResolver; targets.AddRange(items .Where(c => Path.GetExtension(c.Path).Equals(".targets", StringComparison.OrdinalIgnoreCase)) .Select(c => Path.Combine(pathResolver.GetInstallPath(library.Key.Name, library.Key.Version), c.Path.Replace('/', Path.DirectorySeparatorChar)))); props.AddRange(items .Where(c => Path.GetExtension(c.Path).Equals(".props", StringComparison.OrdinalIgnoreCase)) .Select(c => Path.Combine(pathResolver.GetInstallPath(library.Key.Name, library.Key.Version), c.Path.Replace('/', Path.DirectorySeparatorChar)))); } } } } // Targets files contain a macro for the repository root. If only the user package folder was used // allow a replacement. If fallback folders were used the macro cannot be applied. // Do not use macros for fallback folders. Use only the first repository which is the user folder. var repositoryRoot = repositories.First().RepositoryRoot; return(new MSBuildRestoreResult(project.Name, project.BaseDirectory, repositoryRoot, props, targets)); }
public static CommandLineApplication Create() { var app = new CommandLineApplication(throwOnUnexpectedArg: false) { Name = "dotnet ef", FullName = "Entity Framework .NET Core CLI Commands Dispatcher" }; var noBuildOption = app.Option("--no-build", "Do not build before executing"); var configurationOption = app.Option( "-c|--configuration <CONFIGURATION>", "Configuration under which to load"); var frameworkOption = app.Option( "-f|--framework <FRAMEWORK>", "Target framework to load"); var buildBasePathOption = app.Option( "-b|--build-base-path <OUTPUT_DIR>", "Directory in which to find temporary outputs"); var outputOption = app.Option( "-o|--output <OUTPUT_DIR>", "Directory in which to find outputs"); app.OnExecute(() => { var project = Directory.GetCurrentDirectory(); Reporter.Verbose.WriteLine(ToolsStrings.LogUsingProject(project)); var projectFile = ProjectReader.GetProject(project); var framework = frameworkOption.HasValue() ? NuGetFramework.Parse(frameworkOption.Value()) : null; if (framework == null) { var frameworks = projectFile.GetTargetFrameworks().Select(i => i.FrameworkName); framework = NuGetFrameworkUtility.GetNearest(frameworks, FrameworkConstants.CommonFrameworks.NetCoreApp10, f => f) ?? frameworks.FirstOrDefault(); Reporter.Verbose.WriteLine(ToolsStrings.LogUsingFramework(framework.GetShortFolderName())); } var configuration = configurationOption.Value(); if (configuration == null) { configuration = Constants.DefaultConfiguration; Reporter.Verbose.WriteLine(ToolsStrings.LogUsingConfiguration(configuration)); } if (!noBuildOption.HasValue()) { var buildExitCode = BuildCommandFactory.Create( projectFile.ProjectFilePath, configuration, framework, buildBasePathOption.Value(), outputOption.Value()) .ForwardStdErr() .ForwardStdOut() .Execute() .ExitCode; if (buildExitCode != 0) { throw new OperationException(ToolsStrings.BuildFailed(projectFile.Name)); } } // TODO remove when https://github.com/dotnet/cli/issues/2645 is resolved Func <bool> isClassLibrary = () => { var projectContext = ProjectContext.Create( projectFile.ProjectFilePath, framework, RuntimeEnvironmentRidExtensions.GetAllCandidateRuntimeIdentifiers()); var runtimeFiles = projectContext .GetOutputPaths(configuration, buildBasePathOption.Value(), outputOption.Value()) ?.RuntimeFiles; return(runtimeFiles == null || ( framework.IsDesktop() ? !Directory.Exists(runtimeFiles.BasePath) : !File.Exists(runtimeFiles.RuntimeConfigJson) || !File.Exists(runtimeFiles.DepsJson) )); }; Reporter.Verbose.WriteLine(ToolsStrings.LogBeginDispatch(ProjectDependencyToolName, projectFile.Name)); try { bool isVerbose; bool.TryParse(Environment.GetEnvironmentVariable(CommandContext.Variables.Verbose), out isVerbose); var dispatchArgs = ExecuteCommand .CreateArgs(framework, configuration, isVerbose) .Concat(app.RemainingArguments); return(DotnetToolDispatcher.CreateDispatchCommand( dispatchArgs, framework, configuration, outputPath: outputOption.Value(), buildBasePath: buildBasePathOption.Value(), projectDirectory: projectFile.ProjectDirectory, toolName: ProjectDependencyToolName) .ForwardStdErr() .ForwardStdOut() .Execute() .ExitCode); } catch (CommandUnknownException ex) { Reporter.Verbose.WriteLine(ex.Message); var fwlink = "http://go.microsoft.com/fwlink/?LinkId=798221"; if (isClassLibrary()) { Reporter.Error.WriteLine(ToolsStrings.ClassLibrariesNotSupportedInCli(fwlink)); } else { // intentionally put DispatcherToolName in error because "Microsoft.EntityFrameworkCore.Tools.Cli" is // brought in automatically as a dependency of "Microsoft.EntityFrameworkCore.Tools" Reporter.Error.WriteLine(ToolsStrings.ProjectDependencyCommandNotFound(DispatcherToolName, fwlink)); } return(1); } }); return(app); }
public static bool IsLockFileStillValid(DependencyGraphSpec dgSpec, PackagesLockFile nuGetLockFile) { var uniqueName = dgSpec.Restore.First(); var project = dgSpec.GetProjectSpec(uniqueName); // Validate all the direct dependencies foreach (var framework in project.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. return(false); } var directDependencies = target.Dependencies.Where(dep => dep.Type == PackageDependencyType.Direct); if (HasProjectDependencyChanged(framework.Dependencies, directDependencies)) { // lock file is out of sync return(false); } } // Validate all P2P references foreach (var framework in project.RestoreMetadata.TargetFrameworks) { var target = nuGetLockFile.Targets.FirstOrDefault( t => EqualityUtility.EqualsWithNullCheck(t.TargetFramework, framework.FrameworkName)); if (target == null) { // a new target found in the dgSpec so invalidate existing lock file. return(false); } var queue = new Queue <string>(); var visitedP2PReference = new HashSet <string>(); foreach (var projectReference in framework.ProjectReferences) { if (visitedP2PReference.Add(projectReference.ProjectUniqueName)) { queue.Enqueue(projectReference.ProjectUniqueName); while (queue.Count > 0) { var p2pUniqueName = queue.Dequeue(); var p2pProjectName = Path.GetFileNameWithoutExtension(p2pUniqueName); var projectDependency = target.Dependencies.FirstOrDefault( dep => dep.Type == PackageDependencyType.Project && PathUtility.GetStringComparerBasedOnOS().Equals(dep.Id, p2pProjectName)); if (projectDependency == null) { // project dependency doesn't exist in lock file. return(false); } var p2pSpec = dgSpec.GetProjectSpec(p2pUniqueName); // ignore if this p2p packageSpec not found in DGSpec, It'll fail restore with different error at later stage if (p2pSpec != null) { var p2pSpecTarget = NuGetFrameworkUtility.GetNearest(p2pSpec.TargetFrameworks, framework.FrameworkName, e => e.FrameworkName); // ignore if compatible framework not found for p2p reference which means current project didn't // get anything transitively from this p2p if (p2pSpecTarget != null) { if (HasP2PDependencyChanged(p2pSpecTarget.Dependencies, projectDependency)) { // P2P transitive package dependencies has changed return(false); } var p2pSpecProjectRefTarget = p2pSpec.RestoreMetadata.TargetFrameworks.FirstOrDefault( t => PathUtility.GetStringComparerBasedOnOS().Equals(p2pSpecTarget.FrameworkName.GetShortFolderName(), t.FrameworkName.GetShortFolderName())); if (p2pSpecProjectRefTarget != null) { foreach (var reference in p2pSpecProjectRefTarget.ProjectReferences) { if (visitedP2PReference.Add(reference.ProjectUniqueName)) { queue.Enqueue(reference.ProjectUniqueName); } } } } } } } } } return(true); }