c# - pudo - MSBuild para copiar archivos generados dinĂ¡micamente como parte de la dependencia del proyecto
no se pudo copiar obj debug (4)
Algo como esto parece funcionar, ya sea incluirlo manualmente en el .csproj de ProjectA (tenga en cuenta que VS tiene la mala costumbre de resolver ocasionalmente los comodines en rutas absolutas y sobrescribir .csproj) o inyectar dinámicamente con la tarea personalizada. Además, VS almacena en caché los grupos de elementos en abierto, por lo que puede que no copie los archivos o falle la compilación si estuvieran allí pero se eliminaran. En ese caso, los proyectos deben volver a cargarse o reiniciarse VS para que los grupos de elementos se vuelvan a evaluar. MSBuild, TFS, etc. siempre debería funcionar.
<ItemGroup>
<Content Include="$(TargetDir)/*.txt">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
Tengo una tarea msbuild personalizada que genera algunos archivos de salida al directorio de salida ($ (TargetDir)) de un ProjectA. El código actual es algo como esto:
<MyCustomTask ...>
<Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>
Un ProjectB hace referencia a ProjectA, pero el problema es que al crear ProjectB, los archivos generados por MyCustomTask no se copian en el directorio de salida de ProjectB.
¿Cómo podemos obtener archivos adicionales generados dinámicamente para ser copiados como parte de la dependencia del proyecto con MSBuild?
Finalmente he logrado realizar automáticamente la copia del Proyecto B sin tener que modificarla. IIya no estaba tan lejos de la solución, pero el hecho es que no puedo generar de forma estática, ya que la lista de archivos a generar desde el Proyecto A con MyCustomTask es dinámica. Después de profundizar más en Microsoft.Common.targets
, he encontrado que ProjectB obtendrá la lista de resultados del Proyecto A llamando a los GetCopyToOutputDirectoryItems
destino. Este destino depende de AssignTargetPaths
que a su vez depende de la propiedad de la lista de destino AssignTargetPathsDependsOn
.
Por lo tanto, para generar contenido dinámicamente y para que este contenido se copie automáticamente a través de la dependencia estándar del proyecto, necesitamos conectar el Proyecto A en dos lugares diferentes:
- En
AssignTargetPathsDependsOn
como lo llama indirectamente el Proyecto B en el Proyecto A a través de GetCopyToOutputDirectoryItems. Y también es llamado indirectamente por el Proyecto A cuando se llama aPrepareResource
. Aquí, solo estamos generando la lista de archivos que serán generados (por el Proyecto A ) o consumidos por el Proyecto B. AssignTargetPathsDependsOn llamará una tarea personalizadaMyCustomTaskList
que solo es responsable de generar la lista de archivos (pero no de generarlos), esta lista de archivos creará un "Contenido" dinámico conCopyOutputDirectory
. - En
BuildDependsOn
para generar realmente el contenido en el Proyecto A. Esto llamará aMyCustomTask
que generará el contenido.
Todo esto fue configurado así en ProjectA:
<!-- In Project A -->
<!-- Task to generate the files -->
<UsingTask TaskName="MyCustomTask" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>
<!-- Task to output the list of generated of files - It doesn''t generate the file -->
<UsingTask TaskName="MyCustomTaskList" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>
<!-- 1st PART : When Project A is built, It will generate effectively the files -->
<PropertyGroup>
<BuildDependsOn>
MyCustomTaskTarget;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTaskTarget">
<!-- Call MyCustomTask generate the files files that will be generated by MyCustomTask -->
<MyCustomTask
ProjectDirectory="$(ProjectDir)"
IntermediateDirectory="$(IntermediateOutputPath)"
Files="@(MyCustomFiles)"
RootNamespace="$(RootNamespace)"
>
</MyCustomTask>
</Target>
<!-- 2nd PART : When Project B is built, It will call GetCopyToOutputDirectoryItems on ProjectA so we need to generate this list when it is called -->
<!-- For this we need to override AssignTargetPathsDependsOn in order to generate the list of files -->
<!-- as GetCopyToOutputDirectoryItems ultimately depends on AssignTargetPathsDependsOn -->
<!-- Content need to be generated before AssignTargets, because AssignTargets will prepare all files to be copied later by GetCopyToOutputDirectoryItems -->
<!-- This part is also called from ProjectA when target ''PrepareResources'' is called -->
<PropertyGroup>
<AssignTargetPathsDependsOn>
$(AssignTargetPathsDependsOn);
MyCustomTaskListTarget;
</AssignTargetPathsDependsOn>
</PropertyGroup>
<Target Name="MyCustomTaskListTarget">
<!-- Call MyCustomTaskList generating the list of files that will be generated by MyCustomTask -->
<MyCustomTaskList
ProjectDirectory="$(ProjectDir)"
IntermediateDirectory="$(IntermediateOutputPath)"
Files="@(MyCustomFiles)"
RootNamespace="$(RootNamespace)"
>
<Output TaskParameter="ContentFiles" ItemName="MyCustomContent"/>
</MyCustomTaskList>
<ItemGroup>
<!--Generate the lsit of content generated by MyCustomTask -->
<Content Include="@(MyCustomContent)" KeepMetadata="Link;CopyToOutputDirectory"/>
</ItemGroup>
</Target>
Este método funciona con cualquier tipo de proyectos de C # que usan Common.Targets (por lo tanto, funciona con proyectos de Desktop puro, WinRT XAML o Windows Phone 8).
Según entiendo, desea agregar pasos adicionales escribiendo solo esta línea en ProjectB.msbuild:
<Import Project="ProjectA.msbuild" />
Para lograrlo puedes escribir algo como en ProjectA:
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>
Esto agrega su tarea a la lista de dependencias de la tarea Generar.
Consulte esta pregunta para obtener más información: ¿ StyleCop MS Build magic? ¿Quién está llamando el objetivo StyleCop?
Si ya lo está haciendo con MSBuild, ¿podría agregar una tarea de copia para enviar los archivos a su alrededor?