Имитация разрешения сборки процесса msbuild - PullRequest
5 голосов
/ 26 июня 2009

Я пишу инструмент проверки, который проверяет версии файлов, на которые есть ссылки в проекте. Я хочу использовать тот же процесс разрешения, что и MSBuild.

Например, Assembly.Load (..) требует полного имени сборки. Однако в файле проекта у нас может быть только что-то вроде «System.Xml». MSBuild, вероятно, использует целевую версию фреймворка проекта и некоторые другие эвристические методы, чтобы решить, какую версию System.Xml загрузить.

Как бы вы подражали (или непосредственно использовали) процесс разрешения сборки msbuild?

Другими словами, во время выполнения я хочу взять строку "System.Xml" вместе с другой информацией, найденной в файле .csproj, и найти тот же файл, который найдет msbuild.

Ответы [ 7 ]

3 голосов
/ 03 июня 2014

У меня была эта проблема сегодня, и я нашел этот старый пост в блоге о том, как это сделать:

http://blogs.msdn.com/b/jomo_fisher/archive/2008/05/22/programmatically-resolve-assembly-name-to-full-path-the-same-way-msbuild-does.aspx

Я попробовал, отлично работает! Я изменил код, чтобы найти версии сборок 4.5.1, когда это возможно, вот что у меня сейчас:

#if INTERACTIVE
#r "Microsoft.Build.Engine" 
#r "Microsoft.Build.Framework"
#r "Microsoft.Build.Tasks.v4.0"
#r "Microsoft.Build.Utilities.v4.0"
#endif

open System
open System.Reflection
open Microsoft.Build.Tasks
open Microsoft.Build.Utilities
open Microsoft.Build.Framework
open Microsoft.Build.BuildEngine

/// Reference resolution results. All paths are fully qualified.
type ResolutionResults = {
    referencePaths:string array
    referenceDependencyPaths:string array
    relatedPaths:string array
    referenceSatellitePaths:string array
    referenceScatterPaths:string array
    referenceCopyLocalPaths:string array
    suggestedBindingRedirects:string array
    }


let resolve (references:string array, outputDirectory:string) =
    let x = { new IBuildEngine with
                member be.BuildProjectFile(projectFileName, targetNames, globalProperties, targetOutputs) = true
                member be.LogCustomEvent(e) = ()
                member be.LogErrorEvent(e) = ()
                member be.LogMessageEvent(e) = ()
                member be.LogWarningEvent(e) = ()
                member be.ColumnNumberOfTaskNode with get() = 1
                member be.ContinueOnError with get() = true
                member be.LineNumberOfTaskNode with get() = 1
                member be.ProjectFileOfTaskNode with get() = "" }

    let rar = new ResolveAssemblyReference()
    rar.BuildEngine <- x
    rar.IgnoreVersionForFrameworkReferences <- true
    rar.TargetFrameworkVersion <- "v4.5.1"
    rar.TargetedRuntimeVersion <- "v4.5.1"
    rar.TargetFrameworkDirectories <- [||] //[|@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\"|]
    rar.Assemblies <- [|for r in references -> new Microsoft.Build.Utilities.TaskItem(r) :> ITaskItem|]
    rar.AutoUnify <- true
    rar.SearchPaths <- [| "{CandidateAssemblyFiles}"
                          "{HintPathFromItem}"
                          "{TargetFrameworkDirectory}"
                         // "{Registry:Software\Microsoft\.NetFramework,v3.5,AssemblyFoldersEx}"
                          "{AssemblyFolders}"
                          "{GAC}"
                          "{RawFileName}"
                          outputDirectory |]

    rar.AllowedAssemblyExtensions <- [| ".exe"; ".dll" |]
    rar.TargetProcessorArchitecture <- "x86"
    if not (rar.Execute()) then
        failwith "Could not resolve"
    {
        referencePaths = [| for p in rar.ResolvedFiles -> p.ItemSpec |]
        referenceDependencyPaths = [| for p in rar.ResolvedDependencyFiles -> p.ItemSpec |]
        relatedPaths = [| for p in rar.RelatedFiles -> p.ItemSpec |]
        referenceSatellitePaths = [| for p in rar.SatelliteFiles -> p.ItemSpec |]
        referenceScatterPaths = [| for p in rar.ScatterFiles -> p.ItemSpec |]
        referenceCopyLocalPaths = [| for p in rar.CopyLocalFiles -> p.ItemSpec |]
        suggestedBindingRedirects = [| for p in rar.SuggestedRedirects -> p.ItemSpec |]
    }



[<EntryPoint>]
let main argv = 
    try
      let s = resolve([| "System"
                         "System.Data"
                         "System.Core, Version=4.0.0.0"
                         "Microsoft.SqlServer.Replication" |], "")
      printfn "%A" s.referencePaths
    finally
      ignore (System.Console.ReadKey())

    0
2 голосов
/ 20 ноября 2009

Почему бы просто не вызвать msbuild для вашего проекта или файла решения, передать ему расширение / v: d и проанализировать выходной файл для получения необходимой информации? Например, вы увидите что-то вроде следующего для каждого разрешения сборки:

  Primary reference "System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089".
      Resolved file path is "c:\WINNT\Microsoft.NET\Framework\v2.0.50727\System.Data.dll".
      Reference found at search path location "{TargetFrameworkDirectory}".
          For SearchPath "{TargetFrameworkDirectory}".
          Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.exe", but it didn't exist.
          Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.dll", but it didn't exist.
          Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Data.exe", but it didn't exist.
          Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Data.dll", but it didn't exist.
          Considered "c:\WINNT\Microsoft.NET\Framework\v3.5\System.Data.exe", but it didn't exist.
          Considered "c:\WINNT\Microsoft.NET\Framework\v3.5\System.Data.dll", but it didn't exist.
          Considered "c:\WINNT\Microsoft.NET\Framework\v3.0\System.Data.exe", but it didn't exist.
          Considered "c:\WINNT\Microsoft.NET\Framework\v3.0\System.Data.dll", but it didn't exist.
          Considered "c:\WINNT\Microsoft.NET\Framework\v2.0.50727\System.Data.exe", but it didn't exist.
      This reference is not "CopyLocal" because it's a prerequisite file.

В качестве альтернативы MSBuild делегирует задачу разрешения сборок классу Microsoft.Build.Tasks.ResolveAssemblyReference из сборки Microsoft.Build.Tasks.v3.5 (в моем случае, сборка на основе платформы 3.5). Вы можете проанализировать файл проекта и предоставить экземпляр ResolveAssemblyReference с соответствующими (мета) данными, и позволить ему выполнить разрешение за вас - кажется идеальным, поскольку именно это и делает MSBuild.

2 голосов
/ 02 июля 2009

Это должно показать вам, как делать то, что вы действительно хотите, но я думаю, что вы должны использовать ответ FXCop, который я предоставил.

static void Main()
    {
        string targetFile = @"test.csproj";
        XDocument xmlDoc = XDocument.Load(targetFile);
        XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";

        var references = from reference in xmlDoc.Descendants(ns + "ItemGroup").Descendants(ns + "Reference")
                         select reference.Attribute("Include").Value;

        foreach (var reference in references)
        {
            Assembly.LoadWithPartialName(reference);
        }

        foreach (var item in AppDomain.CurrentDomain.GetAssemblies())
        {
            var assemblyVersion = ((AssemblyFileVersionAttribute)item.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true)[0]).Version.ToString();
            Console.WriteLine("\r\nFullname:\t{0}\r\nFileVersion:\t{1}", item.FullName, assemblyVersion);

        }
        Console.WriteLine("\r\nPress any key to continue");
        Console.ReadKey();
    }
2 голосов
/ 02 июля 2009

Если вы нацелены на версию Framework, с которой хотите быть совместимым, а не на 3.5, RTM Visual Studio 2008 SP1 и FxCop 1.36 добавили правило CA 1903: используйте только API из целевой платформы , чтобы обеспечить совместимость с целевой версией фреймворка. Включение этого правила и трактовка его как ошибки приведет к сбою сборки и обеспечит необходимое поведение.

Вот пример кода, демонстрирующего нарушение при настройке платформы версии 2:

using System.Runtime;

class Program
{
    static void Main()
    {
        GCSettings.LatencyMode = GCLatencyMode.LowLatency;
    }
}
1 голос
/ 04 июля 2009

Если вы получаете бесплатную копию Reflector , вы можете проверить внутреннюю часть самого файла MSBuild.exe. Я замечаю, что есть класс

Microsoft.Build.Shared.TypeLoader

имеет метод с именем

internal LoadedType Load(string typeName, AssemblyLoadInfo assembly);

что может помочь?

В любом случае, с помощью отражателя вы можете получить код и, надеюсь, использовать систему напрямую.

0 голосов
/ 04 июля 2009
0 голосов
/ 26 июня 2009

Чтобы напрямую имитировать процесс разрешения CLR, вы можете написать пользовательскую задачу MSBuild, хотя я не вижу, к чему она приведет.

MSBuild не разрешает сборки. Они разрешены CLR. В этой статье описывается, как среда выполнения разрешает сборки: http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx

Когда вы находитесь в Visual Studio, системные сборки приходят из файловой системы, но когда они загружаются во время выполнения, они приходят из GAC. http://p3net.mvps.org/Topics/Basics/IntegratingGACWithVS.aspx

Если у вас остались вопросы, уточните.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...