Set “Copy Local” to False by default?

前端 未结 6 1695
闹比i
闹比i 2020-12-04 19:48

Can I set the default-option of \"Copy Local\" in Visual Studio to False? In most times, when I add a dll as dependency of a project, I want the Copy Local property set to F

相关标签:
6条回答
  • 2020-12-04 19:55

    No - Visual Studio uses an internal set of rules to determine what to set Copy Local to.

    From MSDN:

    1. If the reference is another project, called a project-to-project reference, then the value is true.
    2. If the assembly is found in the global assembly cache, the value is false.
    3. As a special case, the value for the mscorlib.dll reference is false.
    4. If the assembly is found in the Framework SDK folder, then the value is false.
    5. Otherwise, the value is true.
    0 讨论(0)
  • 2020-12-04 19:56

    Bumping this because it seems there's now a nuget package allowing exactly this...

    https://nuget.org/packages/CopyLocalFalse

    Haven't tried yet, just hoping it helps.

    0 讨论(0)
  • 2020-12-04 19:59

    Actually, you can. You need a couple things:

    1. Create .targets file that makes copylocal (<Private> tag, to be precise) false by default.
    2. Import the target in .csproj files. You can add it in the very last line, before closing </Project> tag, it'll look like <Import Project="..\Build\yourtarget.targets" />.

    Now each project with this target has copylocal disabled by default.

    The drawback is that you need to modify each and every csproj file, including new ones. You can work around the new project issue by modifying the VS project template. Instead of Class.cs described in the blog article, you need to modify Class.vstemplate (in the same zip file).

    With that approach, there's one more problem - the path itself. If you use hardcoded relative path in newly-generated csproj files, they may be wrong (unless you have flat project structure).

    You can:

    • Make VS generate correct relative path. Not sure how to do that and if that's even possible.
    • Ignore it and change the path manually for each new csproj (depending on the number of new project you have, while not ideal, that may be tolerable).
    • Use the environment variable instead of relative path. In that case every developer will need the same variable set.

    There must be better solution for that, but haven't found it yet.

    0 讨论(0)
  • 2020-12-04 20:06

    Starting with msbuild v 15 you can copy a single file called Directory.Build.props in the root folder that contains your source:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemDefinitionGroup>
      <Reference>
        <Private>False</Private>
      </Reference>
      <ProjectReference>
         <Private>False</Private>
      </ProjectReference>
    </ItemDefinitionGroup>
    </Project>
    

    Nothing more to do! This works well with Visual Studio 2017 and also the vNext Build. May you have to close Visual Studio and than open your solution again to take the file effect.

    https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets

    0 讨论(0)
  • We don't use a .targets files (as suggested in the answer by ya23), so we just edit the .csproj project file manually in a text editor and add the <Private> element to the reference, like this:

    <Reference Include="[...]">
      <Private>False</Private>
      [...]
    </Reference>
    

    The value of the <Private> element matches the value of the "Copy local" property. For instance, if <Private> is set to False, then "Copy local" is also false..

    0 讨论(0)
  • 2020-12-04 20:07

    Regarding the solution posted by @herzbube, if you want to turn off "Copy Local" for all (or most) of the references in your .csproj file, you don't need to set <Private>False</Private> individually on each Reference, you can just put the following directly in the .csproj:

    <ItemDefinitionGroup>
      <Reference>
        <Private>False</Private>
      </Reference>
    </ItemDefinitionGroup>
    

    This doesn't affect projects referenced with <ProjectReference>, but you can do the same thing--either instead or as well--for those:

    <ItemDefinitionGroup>
      <ProjectReference>
        <Private>False</Private>
      </ProjectReference>
    </ItemDefinitionGroup>
    

    If you want both of these, you can merge them into a single group:

    <ItemDefinitionGroup>
      <Reference>
        <Private>False</Private>
      </Reference>
      <ProjectReference>
        <Private>False</Private>
      </ProjectReference>
    </ItemDefinitionGroup>
    

    Make sure you put these overrides prior to the first actual <Reference ...> or <ProjectReference ...> you want to affect because these blocks will only apply to those references that appear below them. Then, if there are a few that you do actually want to be locally copied, you can just override those back individually (i.e., within the individual tag itself), this time using True.

    For more advanced cases you can switch the overriding value back and forth between True and False multiple times in the same .csproj file. Another advanced technique would be to strategically place some of your references below these blocks, and others above, so the latter won't be affected.

    All of this should make the XML in your .csproj much cleaner and easier to read. But there's even more good news, so read on...


    As for selecting which projects should be be marked <Private>False</Private> this will usually depend on the specific situation, but there is something fundamental everyone can and should do for starters. It's a step so basic, simple and effective and it delivers such huge MSBuild reliability improvements1. and build-time speedup--and with little downside--that every large solution that uses the default (i.e. local per-project) C# output locations should almost always make this adjustment:

    In any and every Visual Studio solution which builds multiple C# class libraries with any non-trivial number of <ProjectReference> inter-dependencies, and which culminates in building one more applications (i.e. executables):

    1. Near the top the .csproj for every class library, insert the <ProjectReference> block shown above.
      REASON: There is no need for any .dll to gather any of the libraries it references into a sub-directory of its own, since no executable is ever run from that location. Such rampant copying is useless busywork and may be unnecessarily slowing down your build, possibly quite dramatically.

    2. On the other hand, do not modify the .csproj for any of your solution's applications.
      REASON: Executables need to have all the privately-built libraries they need in their respective sub-directories, but the build for each app alone should be responsible for individually gathering each dependency, directly from its respective sub-directory, into the app's sub-directory.

    This works perfectly because the .csproj for a class library may reference multiple other class libraries, but the .csproj for an executable usually never references another executable. Thus, for every locally-built library, the only .dll in its bin folder will be itself, whereas every locally-built application will contain the full set of locally-built libraries it references.

    Conveniently, nothing changes for the referenced libraries that are not built by your solution, since these usually use <Reference> instead of <ProjectReference>, and we didn't modify the former tag at all. But do note the assumption just mentioned; if it is violated by some of your projects, you may need to make some adjustments.

    [1.] Reliability improvements could be related to file collisions that may occur when gathering the same library from multiple disjoint paths in a dependency graph, especially in concurrent builds.

    0 讨论(0)
提交回复
热议问题