Программно создайте csproj «на лету» без установленной Visual Studio

#c# #csproj

#c# #csproj файл #csproj

Вопрос:

Я пытаюсь программно сгенерировать файл .csproj, используя MSBuild пространство имен, но отсутствие примеров кода делает этот процесс утомительным и подверженным ошибкам.

На данный момент у меня есть следующий код, но я не уверен, как добавить классы, ссылки и тому подобное в проект.

 public void Build()
{
    string projectFile = string.Format(@"{0}{1}.csproj", Common.GetApplicationDataFolder(), _project.Target.AssemblyName);
    Microsoft.Build.Evaluation.Project p = new Microsoft.Build.Evaluation.Project();

    ProjectCollection pc = new ProjectCollection();

    Dictionary<string, string> globalProperty = new Dictionary<string, string>();

    globalProperty.Add("Configuration", "Debug");
    globalProperty.Add("Platform", "x64");

    BuildRequestData buildRequest = new BuildRequestData(projectFile, globalProperty, null, new string[] { "Build" }, null);

    p.Save(projectFile);

    BuildResult buildResult = BuildManager.DefaultBuildManager.Build(new BuildParameters(pc), buildRequest);
}
  

Спасибо.

Комментарии:

1. Я хотел бы знать пример использования этого. Вы хотите сгенерировать файлы csproj для использования кем-то другим, у кого есть VS?

2. Звучит как странный запрос. Вы смотрели исходный код Mono на GitHub?

3. Это не странный запрос. Я хочу динамически сгенерировать API, содержащий некоторые функциональные возможности, взятые из XML-файла, который определяет, что будет включено в API. Затем я вызову MSBuild, чтобы скомпилировать проект в реальную сборку.

4. Это звучит как проблема XY. Можно динамически создавать сборки, используя отражение. Создайте или скомпилируйте динамически сгенерированный код с помощью CodeDom. Я не понимаю, зачем нужен MSBuild, основываясь на том, что вы нам рассказали.

5. Вы используете MSBuild api. Он использует файлы проекта, но не создает их. Вам нужен XmlWriter. Создать()

Ответ №1:

Если вы действительно хотите создать proj-файл с помощью MSBuild API, вы должны использовать Microsoft.Build.Пространство имен конструкции. Большинство типов, которые вам понадобятся, находятся в Micrsoft.Build.сборка dll. Краткий пример, который создает файл проекта с несколькими свойствами и группами элементов:

 class Program
{
    static void Main(string[] args)
    {
        var root = ProjectRootElement.Create();
        var group = root.AddPropertyGroup();
        group.AddProperty("Configuration", "Debug");
        group.AddProperty("Platform", "x64");

        // references
        AddItems(root, "Reference", "System", "System.Core");

        // items to compile
        AddItems(root, "Compile", "test.cs");

        var target = root.AddTarget("Build");
        var task = target.AddTask("Csc");
        task.SetParameter("Sources", "@(Compile)");
        task.SetParameter("OutputAssembly", "test.dll");

        root.Save("test.csproj");
        Console.WriteLine(File.ReadAllText("test.csproj"));
    }

    private static void AddItems(ProjectRootElement elem, string groupName, params string[] items)
    {
        var group = elem.AddItemGroup();
        foreach(var item in items)
        {
            group.AddItem(groupName, item);
        }
    }
}
  

Обратите внимание, что это просто создает файл proj. Он не запускает его. Кроме того, такие свойства, как «Конфигурация» и «Платформа», имеют смысл только в контексте файла proj, созданного Visual Studio. Здесь они действительно ничего не сделают, если вы не добавите больше групп свойств с условиями, как это делает Visual Studio автоматически.

Как я указал в своих комментариях, я думаю, что это неправильный способ сделать это. На самом деле вам просто нужна динамическая компиляция исходных текстов, которая доступна через CodeDom:

 using System.CodeDom.Compiler;

class Program
{
    static void Main(string[] args)
    {
        var provider = CodeDomProvider.CreateProvider("C#");
        string[] sources = {
                               @"public abstract class BaseClass { public abstract void Test(); }",
                               @"public class CustomGenerated : BaseClass { public override void Test() { System.Console.WriteLine(""Hello World""); } }"
                           };

        var results = provider.CompileAssemblyFromSource(new CompilerParameters() {
            GenerateExecutable = false,
            ReferencedAssemblies = { "System.dll", "System.Core.dll" },
            IncludeDebugInformation = true,
            CompilerOptions = "/platform:anycpu"
        }, sources);
        if (results.Errors.Count > 0)
        {
            Console.WriteLine("{0} errors", results.Errors.Count);
            foreach(CompilerError error in results.Errors)
            {
                Console.WriteLine("{0}: {1}", error.ErrorNumber, error.ErrorText);
            }
        }
        else
        {
            var type = results.CompiledAssembly.GetType("CustomGenerated");
            object instance = Activator.CreateInstance(type);
            type.GetMethod("Test").Invoke(instance, null);
        }
    }
}
  

Комментарии:

1. После попытки использовать MSBuild я признал поражение и решил поклониться большей мудрости и использовать CodeDom вместо этого. Это можно сделать с помощью MSBuild , но это намного сложнее. Это, безусловно, помогло бы, если бы у Microsft была лучшая документация.