예제 #1
0
        /// <summary>
        /// Gets the output assembly of the given project. If the project
        /// was never built before, it's built before returning the output
        /// assembly.
        /// </summary>
        /// <param name="project">The project to get the output assembly from.</param>
        /// <param name="buildIfMissing">Whether to build the project if the output assembly is missing.</param>
        public static Task <Assembly> GetOutputAssembly(this IProjectNode project, bool buildIfMissing = true)
        {
            var fileName = (string)project.Properties.TargetFileName;
            var msBuild  = project.Adapt().AsMsBuildProject();

            if (msBuild == null)
            {
                throw new ArgumentException(Strings.IProjectNodeExtensions.NotMsBuildProject(project.DisplayName));
            }

            // NOTE: we load from the obj/Debug|Release folder, which is
            // the one built in the background by VS continuously.
            var intermediateDir = msBuild.AllEvaluatedProperties
                                  .Where(p => p.Name == "IntermediateOutputPath")
                                  // If we grab the EvaluatedValue, it won't have the current
                                  // global properties overrides, like Configuration and Debug.
                                  .Select(p => msBuild.ExpandString(p.UnevaluatedValue))
                                  .FirstOrDefault();

            if (string.IsNullOrEmpty(fileName) ||
                string.IsNullOrEmpty(intermediateDir) ||
                string.IsNullOrEmpty(project.Properties.MSBuildProjectDirectory))
            {
                tracer.Warn(Strings.IProjectNodeExtensions.NoTargetAssemblyName(project.DisplayName));
                return(TaskHelpers.FromResult <Assembly>(null));
            }

            var outDir       = (string)Path.Combine(project.Properties.MSBuildProjectDirectory, intermediateDir);
            var assemblyFile = Path.Combine(outDir, fileName);

            if (!File.Exists(assemblyFile) && !buildIfMissing)
            {
                return(TaskHelpers.FromResult <Assembly>(null));
            }

            return(Task.Factory.StartNew <Assembly>(() =>
            {
                if (!File.Exists(assemblyFile))
                {
                    var success = project.Build().Result;
                    if (success)
                    {
                        // Let the build finish writing the file
                        for (int i = 0; i < 5; i++)
                        {
                            if (File.Exists(assemblyFile))
                            {
                                break;
                            }

                            Thread.Sleep(200);
                        }
                    }

                    if (!File.Exists(assemblyFile))
                    {
                        tracer.Warn(Strings.IProjectNodeExtensions.NoBuildOutput(project.DisplayName, assemblyFile));
                        return null;
                    }
                }

                var assemblyName = AssemblyName.GetAssemblyName(assemblyFile);
                var vsProject = project.As <IVsHierarchy>();
                var localServices = project.As <IServiceProvider>();
                var globalServices = GlobalServiceProvider.Instance;

                if (vsProject == null ||
                    localServices == null ||
                    globalServices == null)
                {
                    tracer.Warn(Strings.IProjectNodeExtensions.InvalidVsContext);
                    return null;
                }

                var openScope = globalServices.GetService <SVsSmartOpenScope, IVsSmartOpenScope>();
                var dtar = localServices.GetService <SVsDesignTimeAssemblyResolution, IVsDesignTimeAssemblyResolution>();

                // As suggested by Christy Henriksson, we reuse the type discovery service
                // but just for the IDesignTimeAssemblyLoader interface. The actual
                // assembly reading is done by the TFP using metadata only :)
                var dts = globalServices.GetService <DynamicTypeService>();
                var ds = dts.GetTypeDiscoveryService(vsProject);
                var dtal = ds as IDesignTimeAssemblyLoader;

                if (openScope == null || dtar == null || dts == null || ds == null || dtal == null)
                {
                    tracer.Warn(Strings.IProjectNodeExtensions.InvalidTypeContext);
                    return null;
                }

                var provider = new VsTargetFrameworkProvider(dtar, dtal, openScope);

                return provider.GetReflectionAssembly(assemblyName);
            }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default));
        }