Как использовать относительный импорт в шаблоне csproj? - PullRequest
5 голосов
/ 10 мая 2011

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

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="../../../3rdParty/ThirdParty.targets" />
  ...
  <ItemGroup>
    <Reference Include="Library, Version=$(LibraryVersion), Culture=neutral, PublicKeyToken=$(LibraryPublicKeyToken), processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>$(LibraryDir)LibraryDll.dll</HintPath>
    </Reference>
  </ItemGroup>
  ...
</Project>

Файл csproj работает правильно в Visual Studio и при запуске msbuild из командной строки. Когда я пытаюсь создать проект, используя шаблон проекта, я получаю следующую ошибку:

C: \ Users ... \ AppData \ Local \ Temp \ 534cylld.o0p \ Temp \ MyModule.csproj (3,3): импортированный проект "C: \ Users ... \ AppData \ Local \ 3rdParty \" ThirdParty.targets "не найден. Убедитесь, что путь в объявлении правильный, и что файл существует на диске.

Кажется, Visual Studio пытается открыть проект во временном местоположении. Я попытался добавить $ (MSBuildProjectDirectory) к месту импорта, надеясь, что это может заставить его использовать местоположение, которое я намеревался, но это также не сработало.

Есть предложения?

Ответы [ 4 ]

7 голосов
/ 05 января 2012

Необходимо установить для свойства CreateInPlace значение true в vstemplate. документация говорит

Указывает, следует ли создать проект и выполнить замену параметра в указанном местоположении или выполнить замену параметра во временном местоположении, а затем сохранить проект в указанном местоположении.

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

1 голос
/ 12 августа 2011

Я выбрал решение с использованием Мастеров с шаблонами проектов , главным образом потому, что для некоторых из моих шаблонов уже требовались мастера.

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

public class AddTargetsWizard : IWizard
{
    private const string RELATIVE_PATH_TO_TARGETS = @"..\..\..\..\PATH\TO\Custom.Tasks.Targets";

    private const string TASK_NOT_FOUND_MESSAGE = @"A project of this type should be created under a specific path in order for the custom build task to be properly executed.

The build task could not be found at the following location:
    {0}

Including the build task would result in unexpected behavior in Visual Studio.

The project was created, but the build task WILL NOT BE INCLUDED.
This project's builds WILL NOT benefit from the custom build task.";

    private string _newProjectFileName;

    private bool _addTaskToProject;

    private Window _mainWindow;

    public AddTargetsWizard()
    {
        this._addTaskToProject = true;
    }

    protected Window MainWindow
    {
        get
        {
            return this._mainWindow;
        }
    }

    public virtual void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
    {
    }

    public virtual void ProjectFinishedGenerating(EnvDTE.Project project)
    {
        this._newProjectFileName = project.FullName;

        var projectDirectory = Path.GetDirectoryName(this._newProjectFileName);

        var taskPath = Path.GetFullPath(Path.Combine(projectDirectory, RELATIVE_PATH_TO_TARGETS));

        if (!File.Exists(taskPath))
        {
            MessageBox.Show(
                this.MainWindow,
                string.Format(TASK_NOT_FOUND_MESSAGE, taskPath),
                "Project Creation Error",
                MessageBoxButton.OK,
                MessageBoxImage.Error,
                MessageBoxResult.OK,
                MessageBoxOptions.None);

                this._addTaskToProject = false;
        }
    }

    public virtual void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
    {
    }

    public virtual void RunFinished()
    {
        if (this._addTaskToProject)
        {
            var project = new Microsoft.Build.Evaluation.Project(this._newProjectFileName);

            project.Xml.AddImport(RELATIVE_PATH_TO_TARGETS);

            project.Save();
        }
    }

    public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
    {
        var dte = (EnvDTE80.DTE2)automationObject;

        var mainWindow = dte.MainWindow;

        foreach (var proc in System.Diagnostics.Process.GetProcesses())
        {
            if (proc.MainWindowTitle.Equals(mainWindow.Caption))
            {
                var source = HwndSource.FromHwnd(proc.MainWindowHandle);
                this._mainWindow = source.RootVisual as System.Windows.Window;
                break;
            }
        }

        this.OnRunStarted(automationObject, replacementsDictionary, runKind, customParams);
    }

    protected virtual void OnRunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
    {
    }

    public virtual bool ShouldAddProjectItem(string filePath)
    {
        return true;
    }
}

В этом пошаговом руководстве содержится довольно хорошее объяснение того, как связать мастера с шаблоном проекта (или элемента).

Вы заметите, что я предоставляю виртуальный OnRunStarted метод для дочерних мастеров, который должен был бы обеспечить дополнительные функции, такие как отображение окон мастера, заполнение словаря замен и т. Д.

Что мне не нравится в этом подходе и / или моей реализации:

  • Это намного сложнее, чем простой шаблон проекта.
  • Чтобы мои окна мастера - все WPF - были реальными модальными окнами с владельцем Visual Studio, я не нашел лучшего способа, чем использовать заголовок текущего экземпляра для определения HWND и связанного Window.
  • Все хорошо, когда проекты создаются в ожидаемой иерархии папок, но Visual Studio ведет себя странно (то есть всплывают бесполезные диалоги) в противном случае. Вот почему я решил отобразить сообщение об ошибке и не вставлять Import, если местоположение текущего проекта не работает с моим относительным путем.

Если у кого-то есть другие идеи, у меня все еще уши.

0 голосов
/ 18 августа 2011

Я думаю, что вместо этого можно использовать переменную окружения ... Будет ли это работать в вашей ситуации?И если бы вам пришлось делиться шаблонами проекта между разработчиками, вы могли бы сделать что-то необычное в скрипте powershell, где он установил бы переменную среды либо автоматически, либо спросив разработчика, где находится его каталог шаблонов.А потом в макросе csproj:

<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(3rdPartyTargets)" />
  ...

Ой, подождите.Это работает для C ++, но вы могли бы использовать msbuild для ac # / vb.net proj.

0 голосов
/ 17 августа 2011

По умолчанию шаблон распаковывается во временную папку.Затем выполняются замены параметров.Каким-то образом относительные пути проверяются, и во временной папке файлы не существуют.

Вы пытались добавить следующую строку перед импортом?

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
...