Esempio n. 1
0
        /// <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)
                                ));
        }
Esempio n. 3
0
        /// <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();
            }
        }