2011-05-10 22 views
5

Utworzono szablon projektu zawierający csproj, który zawiera Import wskazujący na plik projektu, w którym wymienię wszystkie lokalizacje projektów stron trzecich. Zawsze używam tego szablonu projektu do tworzenia projektów w tym samym katalogu względnym.Jak użyć względnego importu w szablonie csproj?

<?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> 

Plik csproj działa poprawnie w Visual Studio i podczas uruchamiania msbuild z wiersza poleceń. Kiedy próbuję utworzyć projekt przy użyciu szablonu projektu pojawia się następujący błąd:

C:\Users...\AppData\Local\Temp\534cylld.o0p\Temp\MyModule.csproj(3,3): The imported project "C:\Users...\AppData\Local\3rdParty\ThirdParty.targets" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.

Wydaje się, że Visual Studio próbuje otworzyć projekt w tymczasowej lokalizacji pierwszy. Próbowałem już dodać $ (MSBuildProjectDirectory) do lokalizacji importu mając nadzieję, że może zmusić go do użycia lokalizacji, którą zamierzałem, ale to też nie zadziałało.

Wszelkie sugestie?

+0

Mam dokładnie ten sam problem, z tym że zaimportowany plik jest używany do wykonania niestandardowego zadania kompilacji. Potrzebuję tej ścieżki, aby była relatywna, ponieważ jest względna względem hierarchii folderów sterujących źródłami. – madd0

+0

@Christo: Czy możesz podać pełną pah do katalogu, w którym z uruchomionej kompilacji z wiersza poleceń? – sll

Odpowiedz

7

Powinieneś ustawić właściwość CreateInPlace na true w vstemplate. documentation mówi

Specifies whether to create the project and perform parameter replacement in the specified location, or perform parameter replacement in a temporary location and then save the project to the specified location.

Jeśli chcesz ścieżki względne do pracy, trzeba zastąpienie parametru występuje w miejscu, w którym chcesz utworzyć projekt, nie w tymczasowej lokalizacji.

1

Wybrałem rozwiązanie przy użyciu Wizards with Project Templates głównie dlatego, że niektóre z moich szablonów już wymagają kreatorów.

stworzyłem klasę bazową, że wszystkie moje inne kreatory są podobno przedłużyć lub które mogą być wykorzystane przez sam tylko dla podstawowych funkcji:

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 zapewnia dość dobre wyjaśnienie, w jaki sposób powiązać kreatora, aby szablon projektu (lub przedmiotu).

Zauważysz, że przytaczam wirtualny OnRunStarted metodę czarodziejów dzieci, które będą musiały zapewnić dodatkowe funkcje, takie jak wyświetlanie okna kreatora wypełniania zamienniki słownika itp

The Czego nie jak o tym podejściu i/lub mojej implementacji:

  • Jest to o wiele bardziej skomplikowane niż zwykły szablon projektu.
  • Aby moje okna kreatora - wszystkie WPF-y były prawdziwymi modalnymi oknami z Visual Studio jako ich właścicielem, nie znalazłem lepszego sposobu, niż użycie tytułu bieżącej instancji do określenia HWND i powiązanego z nim Window.
  • Wszystko dobrze, jeśli projekty są tworzone w oczekiwanej hierarchii folderów, ale program Visual Studio zachowuje się dziwnie (np. Niepotrzebne okna dialogowe pojawiają się) w przeciwnym razie. Właśnie dlatego wybrałem wyświetlanie komunikatu o błędzie i unikam wstawiania Import, jeśli lokalizacja bieżącego projektu nie działa z moją względną ścieżką.

Jeśli ktoś ma inne pomysły, nadal jestem uszy.

0

Domyślnym zachowaniem jest rozpakowanie szablonu w folderze tymczasowym. Następnie wykonywane są tam wymiany parametrów. W jakiś sposób ścieżki względne są testowane, a w lokalizacji tymczasowej pliki nie istnieją.

Czy przed importowaniem próbowałeś dodać następującą linię?

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

Importowanie plików Microsoft.CSharp.targets nie rozwiąże problemu. – madd0

0

Myślę, że mogę zamiast tego użyć environment variable ... Czy to zadziała w twojej sytuacji?A gdybyś musiał dzielić się szablonami projektów pomiędzy programistami, możesz zrobić coś wymyślnego w skrypcie powershell, w którym ustawiłby zmienną środowiskową automatycznie lub pytając programistę, gdzie znajduje się jego katalog szablonów.

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

A potem w csproj makro:

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

Oh wait. To działa dla C++, ale możesz mieć to use msbuild dla proj. C#/vb.net.

Powiązane problemy