如何在csproj模板中使用相对导入?

时间:2011-05-10 10:01:03

标签: .net visual-studio csproj project-template

我创建了一个项目模板,其中包含一个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 个答案:

答案 0 :(得分:7)

您应该在vstemplate中将 CreateInPlace 属性设置为truedocumentation

  

指定是在目标位置创建项目并执行参数替换,还是在临时位置执行参数替换,然后将项目保存到指定位置。

如果您希望相对路径起作用,则需要在创建项目的地方进行参数替换,不在临时位置

答案 1 :(得分:1)

我选择使用Wizards with Project Templates的解决方案,主要是因为我的一些模板已经需要向导。

我创建了一个基类,我的所有其他向导都应该扩展或者可以单独用于基本功能:

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;
    }
}

This walkthrough提供了关于如何将向导与项目(或项目)模板相关联的非常好的解释。

您会注意到我正在为需要提供其他功能的子向导提供虚拟OnRunStarted方法,例如显示向导窗口,填充替换词典等。

我不喜欢这种方法和/或我的实现:

  • 这比简单的项目模板更复杂。
  • 为了让我的向导窗口 - 所有WPF - 成为Visual Studio作为其所有者的真实模态窗口,我发现没有比使用当前实例的标题来确定HWND和关联的Window更好的方法了。 / LI>
  • 在预期的文件夹层次结构中创建项目时,一切都很好,但Visual Studio的行为很奇怪(否则会弹出无用的对话框)。这就是为什么我选择显示错误消息并避免插入Import如果当前项目的位置不适用于我的相对路径。

如果有人有其他想法,我仍然全力以赴。

答案 2 :(得分:0)

默认行为是将模板解压缩到临时文件夹中。然后,在那里执行参数替换。不知何故,测试了相对路径,并且在临时位置,文件不存在。

您是否尝试在导入之前添加以下行?

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

答案 3 :(得分:0)

我想我可能会使用environment variable代替......那会不会适用于您的情况?如果你不得不在开发人员之间共享项目模板,你可以在powershell脚本中做一些奇特的事情,它会自动设置环境变量,或者询问开发人员模板目录的位置。

[Environment]::SetEnvironmentVariable("3rdPartyTargets", "%ProgramFiles%/3rdParty/ThirdParty.targets", "User")

然后在csproj宏中:

<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(3rdPartyTargets)" />
  ...
等等哦。这适用于C ++,但对于c#/ vb.net项目,您可能有to use msbuild