Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mimicking assembly resolution of the msbuild process

I am writing a validation tool that checks the versions of files referenced in a project. I want to use the same resolution process that MSBuild uses.

For example, Assembly.Load(..) requires a fully-qualified assembly name. However, in the project file, we may only have something like "System.Xml". MSBuild probably uses the project's target framework version and some other heuristics to decide which version of System.Xml to load.

How would you go about mimicking (or directly using) msbuild's assembly resolution process?

In other words, at run-time, I want to take the string "System.Xml", along with other info found in a .csproj file and find the same file that msbuild would find.

like image 497
Neil Avatar asked Jun 26 '09 19:06

Neil


People also ask

What is an assembly reference?

Reference assemblies are usually distributed with the Software Development Kit (SDK) of a particular platform or library. Using a reference assembly enables developers to build programs that target a specific library version without having the full implementation assembly for that version.

How do I check MSBuild version?

There is no way to determine which version of MSBuild was used. There is no days in the executable that says which version was used and nothing in them specific to MSBuild to use as a trail of breadcrumbs to determine it either. You dont even need MSBuild to build an executable.

What is MSBuild EXE?

The Microsoft Build Engine is a platform for building applications. This engine, which is also known as MSBuild, provides an XML schema for a project file that controls how the build platform processes and builds software. Visual Studio uses MSBuild, but MSBuild doesn't depend on Visual Studio.

How does build work in Visual Studio?

Visual Studio determines the build order and calls into MSBuild separately (as needed), all completely under Visual Studio's control. Another difference arises when MSBuild is invoked with a solution file, MSBuild parses the solution file, creates a standard XML input file, evaluates it, and executes it as a project.


2 Answers

I had this problem today, and I found this old blog post on how to do it:

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

I tried it out, works great! I modified the code to find 4.5.1 versions of assemblies when possible, this is what I have now:

#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
like image 116
Robert Jeppesen Avatar answered Oct 09 '22 13:10

Robert Jeppesen


If you target the Framework version you want to be compatible with instead of targeting 3.5, Visual Studio 2008 SP1 and FxCop 1.36 RTM added rule CA 1903: Use only API from targeted framework to ensure you stay compatible with the target framework version. Turning that rule on and treating it as an error will fail your Build and provide the behavior you want.

Here is sample code demonstrating a violation when you are targeting framework version 2:

using System.Runtime;

class Program
{
    static void Main()
    {
        GCSettings.LatencyMode = GCLatencyMode.LowLatency;
    }
}
like image 27
David Silva Smith Avatar answered Oct 09 '22 14:10

David Silva Smith