2009-06-26 14 views
5

Estoy escribiendo una herramienta de validación que verifica las versiones de los archivos a los que se hace referencia en un proyecto. Quiero usar el mismo proceso de resolución que usa MSBuild.Imitando la resolución del ensamblado del proceso msbuild

Por ejemplo, Assembly.Load (..) requiere un nombre de ensamblado totalmente calificado. Sin embargo, en el archivo de proyecto, podemos tener algo así como "System.Xml". Es probable que MSBuild use la versión de marco de destino del proyecto y algunas otras heurísticas para decidir qué versión de System.Xml cargar.

¿Cómo haría para imitar (o usar directamente) el proceso de resolución de ensamblaje de msbuild?

En otras palabras, en tiempo de ejecución, quiero tomar la cadena "System.Xml", junto con otra información encontrada en un archivo .csproj y encontrar el mismo archivo que encontraría msbuild.

Respuesta

3

he tenido este problema hoy en día, y yo encontramos este antiguo puesto de blog sobre cómo hacerlo:

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

lo probé, funciona muy bien! Modifiqué el código para encontrar 4.5.1 versiones de ensamblados cuando sea posible, esto es lo que tengo ahora:

#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 
+0

Agradable. ¡Gracias! –

0

Para imitar directamente el proceso de resolución de CLR, podría escribir una tarea personalizada de MSBuild, aunque no veo lo que conseguiría.

MSBuild no resuelve ensamblajes. Ellos son resueltos por el CLR. Este artículo describe cómo el tiempo de ejecución resuelve ensamblajes: http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx

Cuando está en Visual Studio, los ensambles del sistema provienen del sistema de archivos, pero cuando se cargan en tiempo de ejecución provienen del GAC. http://p3net.mvps.org/Topics/Basics/IntegratingGACWithVS.aspx

Si aún tiene preguntas, por favor aclare.

+0

Estoy interesado en el origen de los ensamblajes en tiempo de compilación. En tiempo de ejecución, quiero ver el XML para una referencia cuyo atributo de Versión es "System.Xml" y encontrar el mismo ensamblado que encuentra msbuild. –

+0

Gracias por intentar responder. Desafortunadamente, ninguna de esas soluciones son de tiempo de ejecución. –

+0

¿Qué estás tratando de hacer exactamente? Probablemente pueda hacerlo si lo entendiera. ¿Está intentando analizar un .csproj para conjuntos de sistemas, determinar sus rutas y luego obtener la versión de ensamblado? ¿Estás tratando de hacer esto con ensamblajes que no sean del sistema? Todos los ensamblajes del sistema se cargarán desde la Caché de ensamblados global en el tiempo de ejecución, pero desde una ubicación diferente en el momento de la compilación, por lo que esta podría ser una distinción importante. –

1

Si se dirige a la versión de Framework con la que desea ser compatible, en lugar de orientarse a 3.5, Visual Studio 2008 SP1 y FxCop 1.36 RTM agregaron la regla CA 1903: Use only API from targeted framework para garantizar que siga siendo compatible con la versión de marco de destino. Activar esa regla y tratarla como un error le fallará a su compilación y le proporcionará el comportamiento que desea.

Aquí es código de ejemplo que demuestra una violación cuando se está apuntando marco de la versión 2:

using System.Runtime; 

class Program 
{ 
    static void Main() 
    { 
     GCSettings.LatencyMode = GCLatencyMode.LowLatency; 
    } 
} 
1

Esto le mostrará cómo hacer lo que realmente quiere, pero creo que se debe utilizar la respuesta FXCop he proporcionado.

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(); 
    } 
0

Si usted se consigue una copia gratuita de Reflector, se puede examinar la parte interna del MSBuild.exe archivo en sí. Me he dado cuenta que hay una clase

Microsoft.Build.Shared.TypeLoader 

que tiene un método llamado

internal LoadedType Load(string typeName, AssemblyLoadInfo assembly); 

que puede ayudar?

De todos modos, con el reflector puede obtener el código y, con suerte, reutilizar el sistema directamente.

2

¿Por qué no simplemente llama a msbuild contra su proyecto o archivo de solución, le pasa la extensión/v: d y analiza el archivo de salida para obtener la información que desea?Por ejemplo, verá algo como lo siguiente para cada resolución de la Asamblea:

 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. 

Alternativamente, los delegados de MSBuild la tarea de resolver las asambleas a la clase de los Microsoft.Build.Tasks.ResolveAssemblyReference Microsoft.Build.Tasks. v3.5 assembly (en mi caso, construyendo contra el framework 3.5). Puede analizar el archivo del proyecto y proporcionar una instancia de ResolveAssemblyReference con los datos (meta) apropiados, y dejar que ejecute la resolución por usted: parece perfecto, ya que eso es exactamente lo que hace MSBuild.

Cuestiones relacionadas