تحديد مخرجات المشروع في MSBUILD دون أن تؤدي إلى إعادة بناء زائدة عن الحاجة

StackOverflow https://stackoverflow.com/questions/2325598

سؤال

كجزء من حل يحتوي على العديد من المشاريع ، لدي مشروع يشير (عبر أ <ProjectReference> ثلاثة مشاريع أخرى في الحل ، بالإضافة إلى البعض الآخر). في ال AfterBuild, ، أحتاج إلى نسخ مخرجات 3 مشاريع تابعة محددة إلى موقع آخر.

عبر مختلف الإجابات ، إلخ. بالطريقة التي استقرت بها لإنجاز ذلك هي:

    <MSBuild 
        Projects="@(ProjectReference)" 
        Targets="Build" 
        BuildInParallel="true" 
        Condition="'%(Name)'=='ProjectA' OR '%(Name)'=='ProjectB' OR '%(Name)'=='ProjectC'">
        <Output TaskParameter="TargetOutputs" ItemName="DependentAssemblies" />
    </MSBuild>
    <Copy SourceFiles="@(DependentAssemblies)" DestinationFolder="XX" SkipUnchangedFiles="true" />

ومع ذلك ، واجهت مشاكل مع هذا. ال <MSBuild خطوات IncrementalClean المهمة تنتهي حذف عدد من مخرجات ProjectC. عند تشغيل هذا تحت VS2008 ، أ build.force يتم إيداع الملف في obj/Debug مجلد ProjectC الذي يؤدي بعد ذلك إلى إعادة بناء ProjectC إذا قمت ببناء على الحل بأكمله إذا كان المشروع يحتوي على هذا AfterBuild الهدف ، في حين أن المرء يستبعد هذا المشروع من البناء ، فإنه [بشكل صحيح] لا يؤدي إلى إعادة بناء ProjectC (و حاسم إعادة بناء لجميع المعالين من ProjectC). قد يكون هذا خداعًا خاصًا بـ VS في هذه الحالة والذي لن يحدث في سياق بناء TeamBuild أو غيره من سطر القيادة MSBuild (ولكن الاستخدام الأكثر شيوعًا سيكون عبر VS ، لذلك أحتاج إلى حل هذا الاتجاه)

تم إنشاء جميع المشاريع التابعة (وبقية الحل بشكل عام) بشكل تفاعلي مع VS ، وبالتالي ProjectRefenceتحتوي S على مسارات نسبية وما إلى ذلك. لقد رأيت ذكرًا على أنه من المحتمل أن يتسبب في مشاكل - ولكن بدون تفسير كامل للسبب ، أو متى سيتم إصلاحه أو كيفية العمل حوله. بمعنى آخر ، لست مهتمًا حقًا بتحويل ProjectReference مسارات إلى المسارات المطلقة عن طريق تحرير يدوي.

على الرغم من أنه من الممكن تمامًا ، إلا أنني أفعل شيئًا غبيًا وسيشير شخص ما على الفور إلى ما هو (الذي سيكون رائعًا) /v:diag المخرجات وما إلى ذلك (على الرغم من أنني لم أحاول بناء ريبو من الألف إلى الياء - وهذا في سياق بناء شامل معقد نسبيا)

هل كانت مفيدة؟

المحلول

كما هو مذكور في تعليقي ، فإن استدعاء GetTargetPath على المشروع المشار إليه يعيد فقط مجموعة الإخراج الأساسية لهذا المشروع. للحصول على جميع التجميعات المحلية المرجعية للمشروع المشار إليها ، فهي أكثر فوضى.

أضف ما يلي إلى كل مشروع تشير إلى أنك تريد الحصول على copylocals من:

    <Target
    Name="ComputeCopyLocalAssemblies"
    DependsOnTargets="ResolveProjectReferences;ResolveAssemblyReferences"
    Returns="@(ReferenceCopyLocalPaths)" /> 

وضعي الخاص هو أنني بحاجة إلى إعادة إنشاء هيكل مجلد خط الأنابيب للنظام. هذا أمر فوضوي نوعًا ما ولم أكن راضيا عن حلول MSDN المقترحة من mucking مع OutputPath - حيث أن ذلك ينفصل عن خادم البناء الخاص بنا ويمنع إنشاء بنية المجلد في مشروع مختلف (مثل اختبار النظام)

لذلك إلى جانب إضافة الهدف أعلاه (باستخدام استيراد .Targets) ، أضفت ما يلي إلى ملف .Targets الذي يستورده كل "مضيف" يحتاج إلى مجلد خط الأنابيب الذي تم إنشاؤه:

<Target
    Name="ComputePipelineAssemblies"
    BeforeTargets="_CopyFilesMarkedCopyLocal"
    Outputs="%(ProjectReference.Identity)">

    <ItemGroup>
        <_PrimaryAssembly Remove="@(_PrimaryAssembly)" />
        <_DependentAssemblies Remove="@(_DependentAssemblies)" />
    </ItemGroup>

    <!--The Primary Output of the Pipeline project-->
    <MSBuild Projects="%(ProjectReference.Identity)"
             Targets="GetTargetPath"
             Properties="Configuration=$(Configuration)"
             Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
        <Output TaskParameter="TargetOutputs"
                ItemName="_PrimaryAssembly" />
    </MSBuild>

    <!--Output of any Referenced Projects-->
    <MSBuild Projects="%(ProjectReference.Identity)"
             Targets="ComputeCopyLocalAssemblies"
             Properties="Configuration=$(Configuration)"
             Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
        <Output TaskParameter="TargetOutputs"
                ItemName="_DependentAssemblies" />
    </MSBuild>

    <ItemGroup>
        <ReferenceCopyLocalPaths Include="@(_PrimaryAssembly)"
                                 Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
            <DestinationSubDirectory>%(ProjectReference.PipelineFolder)</DestinationSubDirectory>
        </ReferenceCopyLocalPaths>
        <ReferenceCopyLocalPaths Include="@(_DependentAssemblies)"
                                 Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
            <DestinationSubDirectory>%(ProjectReference.PipelineFolder)</DestinationSubDirectory>
        </ReferenceCopyLocalPaths>
    </ItemGroup>
</Target>

كنت بحاجة أيضًا إلى إضافة بيانات التعريف المطلوبة لخط الأنابيب إلى مراجع المشروع الفعلية. علي سبيل المثال:

    <ProjectReference Include="..\Dogs.Pipeline.AddInSideAdapter\Dogs.Pipeline.AddInSideAdapter.csproj">
        <Project>{FFCD0BFC-5A7B-4E13-9E1B-8D01E86975EA}</Project>
        <Name>Dogs.Pipeline.AddInSideAdapter</Name>
        <Private>False</Private>
        <PipelineFolder>Pipeline\AddInSideAdapter\</PipelineFolder>
    </ProjectReference>

نصائح أخرى

يجب أن يعمل الحل الأصلي الخاص بك ببساطة عن طريق التغيير

Targets="Build"

ل

Targets="GetTargetPath"

ال GetTargetPath الهدف ببساطة يعيد TargetPath الممتلكات ولا تتطلب البناء.

يمكنك حماية ملفاتك في ProjectC إذا اتصلت بهدف مثل هذا أولاً:

  <Target Name="ProtectFiles">
    <ReadLinesFromFile File="obj\ProjectC.csproj.FileListAbsolute.txt">
        <Output TaskParameter="Lines" ItemName="_FileList"/>
    </ReadLinesFromFile>
    <CreateItem Include="@(_DllFileList)" Exclude="File1.sample; File2.sample">
        <Output TaskParameter="Include" ItemName="_FileListWitoutProtectedFiles"/>
    </CreateItem>      
      <WriteLinesToFile 
        File="obj\ProjectC.csproj.FileListAbsolute.txt"
        Lines="@(_FileListWitoutProtectedFiles)"
        Overwrite="true"/>
  </Target>

لي يعتمد الحل البديل الحالي على هذا السؤال, ، أي لدي:

    <ItemGroup>
        <DependentAssemblies Include="
            ..\ProjectA\bin\$(Configuration)\ProjectA.dll;
            ..\ProjectB\bin\$(Configuration)\ProjectB.dll;
            ..\ProjectC\bin\$(Configuration)\ProjectC.dll">
        </DependentAssemblies>
    </ItemGroup>

ومع ذلك ، فإن هذا سوف ينهار تحت بناء TeamBuild (حيث تنتهي جميع المخرجات في دليل واحد) ، وأيضًا إذا تغيرت أسماء أي من مخرجات المشاريع التابعة.

تحرير: أيضًا البحث عن أي تعليقات حول ما إذا كان هناك إجابة أنظف لكيفية جعل الترميز المتشددين أكثر نظافة من:

    <PropertyGroup>
        <_TeamBuildingToSingleOutDir Condition="'$(TeamBuildOutDir)'!='' AND '$(CustomizableOutDir)'!='true'">true</_TeamBuildingToSingleOutDir>
    </PropertyGroup>

و:

    <ItemGroup>
        <DependentAssemblies 
            Condition="'$(_TeamBuildingToSingleOutDir)'!='true'"
            Include="
                ..\ProjectA\bin\$(Configuration)\ProjectA.dll;
                ..\ProjectB\bin\$(Configuration)\ProjectB.dll;
                ..\ProjectC\bin\$(Configuration)\ProjectC.dll">
        </DependentAssemblies>
        <DependentAssemblies 
            Condition="'$(_TeamBuildingToSingleOutDir)'=='true'"
            Include="
                $(OutDir)\ProjectA.dll;
                $(OutDir)\ProjectB.dll;
                $(OutDir)\ProjectC.dll">
        </DependentAssemblies>
    </ItemGroup>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top