/// <summary> /// Retrieve metadata for all tasks defined in the project. /// </summary> /// <param name="cancellationToken"> /// An optional cancellation token that can be used to cancel the operation. /// </param> /// <returns> /// A dictionary of task assembly metadata, keyed by assembly path. /// </returns> /// <remarks> /// Cache metadata (and persist cache to file). /// </remarks> public async Task <List <MSBuildTaskAssemblyMetadata> > GetMSBuildProjectTaskAssemblies(CancellationToken cancellationToken = default(CancellationToken)) { if (!HasMSBuildProject) { throw new InvalidOperationException($"MSBuild project '{ProjectFile.FullName}' is not loaded."); } List <string> taskAssemblyFiles = new List <string> { // Include "built-in" tasks. Path.Combine(DotNetRuntimeInfo.GetCurrent().BaseDirectory, "Microsoft.Build.Tasks.Core.dll"), Path.Combine(DotNetRuntimeInfo.GetCurrent().BaseDirectory, "Roslyn", "Microsoft.Build.Tasks.CodeAnalysis.dll") }; taskAssemblyFiles.AddRange( MSBuildProject.GetAllUsingTasks() .Where(usingTask => !String.IsNullOrWhiteSpace(usingTask.AssemblyFile)) .Distinct(UsingTaskAssemblyEqualityComparer.Instance) // Ensure each assembly path is only evaluated once. .Select(usingTask => Path.GetFullPath( Path.Combine( usingTask.ContainingProject.DirectoryPath, MSBuildProject.ExpandString(usingTask.AssemblyFile) .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) ) )) .Distinct(StringComparer.OrdinalIgnoreCase) ); cancellationToken.ThrowIfCancellationRequested(); List <MSBuildTaskAssemblyMetadata> metadata = new List <MSBuildTaskAssemblyMetadata>(); foreach (string taskAssemblyFile in taskAssemblyFiles) { Log.Verbose("Scanning assembly {TaskAssemblyFile} for task metadata...", taskAssemblyFile); if (!File.Exists(taskAssemblyFile)) { Log.Information("Skipped scan of task metadata for assembly {TaskAssemblyFile} (file not found).", taskAssemblyFile); continue; } cancellationToken.ThrowIfCancellationRequested(); MSBuildTaskAssemblyMetadata assemblyMetadata = await Workspace.TaskMetadataCache.GetAssemblyMetadata(taskAssemblyFile); metadata.Add(assemblyMetadata); Log.Verbose("Completed scanning of assembly {TaskAssemblyFile} for task metadata ({DiscoveredTaskCount} tasks discovered).", taskAssemblyFile, assemblyMetadata.Tasks.Count); } // Persist any changes to cached metadata. Workspace.PersistTaskMetadataCache(); return(metadata); }
/// <summary> /// Retrieve the full path of a task assembly supplied as part of the current framework. /// </summary> /// <param name="assemblyFileName"> /// The relative filename of the task assembly. /// </param> /// <returns> /// The full path of the task assembly. /// </returns> static string GetFrameworkTaskAssemblyFile(string assemblyFileName) { if (string.IsNullOrWhiteSpace(assemblyFileName)) { throw new System.ArgumentException($"Argument cannot be null, empty, or entirely composed of whitespace: {nameof(assemblyFileName)}.", nameof(assemblyFileName)); } var runtimeInfo = DotNetRuntimeInfo.GetCurrent(); return(Path.Combine(runtimeInfo.BaseDirectory, assemblyFileName.Replace('/', Path.DirectorySeparatorChar) )); }
public void Parse(string language, string expectedVersion, string expectedBaseDirectory, string expectedRID, string dotnetInfoOutput) { DotNetRuntimeInfo parsedOutput; using (StringReader outputReader = new StringReader(dotnetInfoOutput)) { parsedOutput = DotNetRuntimeInfo.ParseDotNetInfoOutput(outputReader); } Assert.NotNull(parsedOutput); Assert.Equal(expectedVersion, parsedOutput.Version); Assert.Equal(expectedBaseDirectory, parsedOutput.BaseDirectory); Assert.Equal(expectedRID, parsedOutput.RID); }
/// <summary> /// The main program entry-point. /// </summary> /// <param name="args"> /// Command-line arguments. /// </param> /// <returns> /// 0 if successful; otherwise, 1. /// </returns> public static int Main(string[] args) { if (args.Length != 1) { WriteErrorJson("Must specify the task assembly file to examine."); return(1); } try { // TODO: Consider looking for IntelliDoc XML file to resolve help for tasks and task parameters. FileInfo tasksAssemblyFile = new FileInfo(args[0]); if (!tasksAssemblyFile.Exists) { WriteErrorJson("Cannot find file '{0}'.", tasksAssemblyFile.FullName); return(1); } DotNetRuntimeInfo runtimeInfo = DotNetRuntimeInfo.GetCurrent(tasksAssemblyFile.Directory.FullName); string fallbackDirectory = runtimeInfo.BaseDirectory; string baseDirectory = tasksAssemblyFile.DirectoryName; DirectoryAssemblyLoadContext loadContext = new DirectoryAssemblyLoadContext(baseDirectory, fallbackDirectory); Assembly tasksAssembly = loadContext.LoadFromAssemblyPath(tasksAssemblyFile.FullName); if (tasksAssembly == null) { WriteErrorJson("Unable to load assembly '{0}'.", tasksAssemblyFile.FullName); return(1); } Type[] taskTypes; try { taskTypes = tasksAssembly.GetTypes(); } catch (ReflectionTypeLoadException typeLoadError) { taskTypes = typeLoadError.Types; } taskTypes = taskTypes.Where( type => !type.IsNested && type.IsClass && !type.IsAbstract && type.GetInterfaces().Any(interfaceType => interfaceType.FullName == "Microsoft.Build.Framework.ITask") ) .ToArray(); using (StringWriter output = new StringWriter()) using (JsonTextWriter json = new JsonTextWriter(output)) { json.Formatting = Formatting.Indented; json.WriteStartObject(); json.WritePropertyName("assemblyName"); json.WriteValue(tasksAssembly.FullName); json.WritePropertyName("assemblyPath"); json.WriteValue(tasksAssemblyFile.FullName); json.WritePropertyName("timestampUtc"); json.WriteValue(tasksAssemblyFile.LastWriteTimeUtc); json.WritePropertyName("tasks"); json.WriteStartArray(); foreach (Type taskType in taskTypes) { json.WriteStartObject(); json.WritePropertyName("taskName"); json.WriteValue(taskType.Name); json.WritePropertyName("typeName"); json.WriteValue(taskType.FullName); PropertyInfo[] properties = taskType.GetProperties() .Where(property => (property.CanRead && property.GetGetMethod().IsPublic) || (property.CanWrite && property.GetSetMethod().IsPublic) ) .ToArray(); json.WritePropertyName("parameters"); json.WriteStartArray(); foreach (PropertyInfo property in properties) { if (!SupportedTaskParameterTypes.Contains(property.PropertyType.FullName) && !SupportedTaskParameterTypes.Contains(property.PropertyType.FullName + "[]") && !property.PropertyType.IsEnum) { continue; } json.WriteStartObject(); json.WritePropertyName("parameterName"); json.WriteValue(property.Name); json.WritePropertyName("parameterType"); json.WriteValue(property.PropertyType.FullName); if (property.PropertyType.IsEnum) { json.WritePropertyName("enum"); json.WriteStartArray(); foreach (string enumMember in Enum.GetNames(property.PropertyType)) { json.WriteValue(enumMember); } json.WriteEndArray(); } bool isRequired = property.GetCustomAttributes().Any(attribute => attribute.GetType().FullName == "Microsoft.Build.Framework.RequiredAttribute"); if (isRequired) { json.WritePropertyName("required"); json.WriteValue(true); } bool isOutput = property.GetCustomAttributes().Any(attribute => attribute.GetType().FullName == "Microsoft.Build.Framework.OutputAttribute"); if (isOutput) { json.WritePropertyName("output"); json.WriteValue(true); } json.WriteEndObject(); } json.WriteEndArray(); json.WriteEndObject(); } json.WriteEndArray(); json.WriteEndObject(); json.Flush(); output.Flush(); Console.Out.WriteLine(output); Console.Out.Flush(); } return(0); } catch (Exception unexpectedError) { System.Diagnostics.Debug.WriteLine(unexpectedError); WriteErrorJson(unexpectedError.ToString()); return(1); } finally { Console.Out.Flush(); } }