72
votes

We're using Perforce and Visual Studio. Whenever we create a branch, some projects will not be bound to source control unless we use "Open from Source Control", but other projects work regardless. From my investigations, I know some of the things involved:

In our .csproj files, there are these settings:

  • <SccProjectName>
  • <SccLocalPath>
  • <SccAuxPath>
  • <SccProvider>

Sometimes they are all set to "SAK", sometimes not. It seems things are more likely to work if these say "SAK".

In our .sln file, there are settings for many of the projects:

  • SccLocalPath#
  • SccProjectFilePathRelativizedFromConnection#
  • SccProjectUniqueName#

(The # is a number that identifies each project.) SccLocalPath is a path relative to the solution file. Often it is ".", sometimes it is the folder that the project is in, and sometimes it is ".." or "..\..", and it seems to be bad for it to point to a folder above the solution folder. The relativized one is a path from that folder to the project file. It will be missing entirely if SccLocalPath points to the project's folder. If the SccLocalPath has ".." in it, this path might include folder names that are not the same between branches, which I think causes problems.

So, to finally get to the specifics I'd like to know:

  • What happens when you do "Change source control" and bind projects? How does Visual Studio decide what to put in the project and solution files?
  • What happens when you do "Open from source control"?
  • What's this "connection" folder that SccLocalPath and SccProjectFilePathRelativizedFromConnection refer to? How does Visual Studio/Perforce pick it?
  • Is there some recommended way to make the source control bindings continue to work even when you create a new branch of the solution?

Added June 2012: I don't use Perforce any more, so I can't vouch for it, but have a look at KCD's answer below. Apparently there's a new P4 VS plugin under development. Hopefully it should clear up all this mess!

9

9 Answers

102
votes

Introduction

I would disagree with the claim that Perforce integration in Visual Studio is "terrible". Rather, I'd define it as "out of the box experience is less than optimal" :-). The following sections discuss my understanding of the integration and recommendations for project/solution setup.

If you're not interested in the details of how the source control integration works you can skip to the end of this answer where I summarize answers to Weeble's question.

Disclaimer: The following sections are just educated guesses based on my empirical experience, however I've used the technique over many years in many projects (each project having multiple experimental/trunk/maintenance/release branches, sometimes even multiple solution files, without issues). The disadvantage is that you have to manually update the project files - but the 2 minute investment is amortized over the lifetime of a project pretty nicely IMHO :-).

Solution vs. Project

Visual Studio uses source control binding information from both solution file and each project file during the initial solution loading. This binding information is then stored in name.suo file (assuming we're using name.sln as solution) - note that suo files are marked with hidden flag so they won't be visible in file explorer (unless you override the "Hidden files and folders" option).

The easiest way to re-bind to source control provider if anything goes wrong is to delete the appropriate suo file and reopen solution. After suo file has been created, changes to <Scc*> elements have no effect.

If during the initial solution opening there is a discrepancy between the binding information stored in solution file and information stored in project file, Visual Studio will attempt to fix this (sometimes it will even prompt for your decision to choose whether the information in solution or the information in project should be used as a "master" to resolve the discrepancy):

alt text

Why is Visual Studio violating DRY (Don't Repeat Yourself) principle? I have no idea. I presume this has historic reasons and is tightly coupled to the needs of that nightmare called Visual Source Safe :-).

How to set it up "correctly"?

When adding either new or existing solutions/projects to Perforce, I always start by creating a blank solution (see the "Source-controlling a blank solution" section). I then add projects to this blank solution, one after another. The steps differ slightly based on whether the project being added already exists (see the "Source-controlling existing (unbound) projects" and "Source-controlling existing (bound) projects" sections) or I need to create a new one (see the "Source-controlling new projects" section).

Source-controlling a blank solution

To add a new blank solution to source control, do the following:

  1. Start Visual Studio, "New" -> "Project..." -> "Other project types" -> "Blank solution"; fill in solution name and location, "OK" button
  2. "File" -> "Source Control" -> "Add Solution to Source Control..."
  3. In the connection dialog enter appropriate P4 server port, client and user (note that view of the selected client must include the location that you picked in step 1)
  4. "View" -> "Pending Checkins" -> "Check In" -> in the submit dialog instead of hitting the "Submit" button, use "Cancel".
    Reason: The "Check In" action will create a new file, "name.vssscc", then add both "name.sln" and "name.vssscc" to Perforce's default changelist; by cancelling the submit dialog we will keep the "add" operation pending and will be able to edit the files before submitting to P4
  5. Close Visual Studio
  6. Open the name.sln file in your favourite editor (notepad, if you're really desperate :-) ) and add two new lines (SccProjectName0 and SccProvider0) - the blank solution file should now have a source control section as follows:

    GlobalSection(SourceCodeControl) = preSolution
        SccNumberOfProjects = 1
        SccLocalPath0 = .
        SccProjectName0 = Tutorial
        SccProvider0 = MSSCCI:Perforce\u0020SCM
    EndGlobalSection
    

    The values should be chosen as follows:

    • SccProjectName0: an arbitrary string that will be displayed in "Change Source Control" dialog as "Server Binding". This name is used to determine what projects/solution files can share the same source control connection. I recommend not using space for this name as escaping rules for spaces are different in solution and project files.
    • SccProvider0: hard-coded value "MSSCCI:Perforce\u0020SCM".
  7. Submit the two pending files using the Perforce client of your choice (p4.exe, P4Win, P4V)

You can now test the bindings:

  1. Make sure Visual Studio is closed
  2. Delete **all* files except the name.sln (especially the name.suo)
  3. Open Visual Studio and use it to open name.sln
  4. A connection dialog should appear, use appropriate port/client/user and click OK
  5. Solution explorer should now display the solution node with a padlock overlay icon: Source-controlled blank solution
  6. You can now verify source control status of the solution by using "File" -> "Source Control" -> "Change Source Control...": Source control status of blank solution Note: The column "Server Binding" is showing the value we chose for "SccProjectName0".

Source-controlling new projects

If you're creating a brand-new project and would like to immediately start tracking it in a Perforce depot, follow these steps:

  1. Open the source-controlled solution in Visual Studio
  2. "File" -> "Add" -> "New Project..." - pick the project type you're adding, name and location (location should be a subdirectory of the directory where the solution file is stored)
  3. "File" -> "Save All" (this will commit all in-memory changes to solution file and the newly created project file to the disk)
  4. Manually edit the project file you just created using an editor of your choice (come on, notepad AGAIN? ;-) ). Add the following property elements into a PropertyGroup (any property group):

    <PropertyGroup>
        ...
        <SccProjectName>Tutorial</SccProjectName>
        <SccLocalPath>..\..</SccLocalPath>
        <SccProvider>MSSCCI:Perforce SCM</SccProvider>
        ...
    </PropertyGroup>
    

    The values should be chosen as follows:

    • SccProjectName - this is the value that is displayed in "Change Source Control" dialog as "Server Binding"; should be the same as the value you used for SccProjectName0 in blank solution; if not the same, solution and this project won't be able to share the same source control provider connection
    • SccLocalPath - relative path to the reference directory (displayed in "Change Source Control" dialog as "Local binding"); because I recommend using the solution directory as the reference directory, this is in effect relative path from directory containing project file to directory containing solution file (my example is storing projects in "(solutionDir)/Source/ProjectName/projectName.csproj", thus the relative path is "two levels up")
    • SccProvider - hard-coded value "MSSCCI:Perforce SCM"; this is used to determine what SCCAPI provider are the Scc* binding values valid for
  5. Switch back to Visual Studio; it should automatically detect that the project file has been updated externally and offer to reload it (if not, unload and reload the project manually)

  6. "View" -> "Pending Checkins"
  7. "Check In" -> I recommend right-clicking on (solutionName).vssscc file and selecting "Revert if unchanged" (even though Visual Studio opens it for edit, it remains unchanged); provide description and submit the change

To verify that the newly added project is bound properly, you can follow these steps:

  1. Make sure Visual Studio is closed
  2. Delete (solutionName).suo file as well as MSSCCPRJ.SCC (in solution directory)
  3. Open Visual Studio and use it to open (solutionName).sln
  4. A connection dialog should appear, use appropriate port/client/user and click OK
  5. Solution explorer should now display the project node with a padlock overlay icon: Source-controlled projects
  6. You can now verify source control status of the solution by using "File" -> "Source Control" -> "Change Source Control...": Status of source-controlled projects

    One thing to note about this status screenshot is that when I selected the solution row, all the remaining rows were "selected" as well (blue highlight). This is because all those entries have the same "Server Binding" + "Local Binding" and thus share the same source-control-provider (P4) connection.

    Also note that "Relative Path" for both projects has two levels, and are relative to the same "Local Binding" - the directory where solution file resides.

Source-controlling existing (unbound) projects

If you have existing projects that have not yet been used in any other Perforce solution, follow these steps to add them to Perforce (i.e. importing projects that have not been source-controlled before (Internet downloads etc.) or were using a different source control provider (Visual Source Safe, etc.).

  1. Copy the project into appropriate location
  2. Clean-up existing source control bindings (if any):
    • Remove existing project-file bindings, i.e. all properties starting with "Scc"
    • Delete file (projectName).vspscc in the same directory as the project file (if any)
  3. Open the source-controlled solution in Visual Studio
  4. "File" -> "Add" -> "Existing project..." - browse to the project (the copy you created in step 1)
  5. "File" -> "Save All" (this will commit all in-memory changes to solution file)
  6. Follow the steps 4-7 from "Source-controlling new projects" (i.e. you will now add "Scc*" property elements into a PropertyGroup)

Verification steps are exactly the same as in "Source-controlling new projects" section.

Source-controlling existing (bound) projects

If you have projects that have already been bound to Perforce using the technique discussed here and you want to use them in a different solution (new branch, alternative solution reusing the project, etc) use the following steps:

  1. Integrate the project into desired location
  2. Open the source-controlled solution in Visual Studio
  3. "File" -> "Add" -> "Existing project..." - browse to the project created in step 1 via integration
  4. "View" -> "Pending Checkins" -> "Check In" - add description and submit

Summary

  • Source control binding information is stored in both solution and projects, must be in sync (if not, Visual Studio will attempt to fix any discrepancies)
  • I always treat project files as the primary source of binding information and solution files as throwaway files that can be recreated easily by first source-controlling a blank solution and then adding desired projects
  • Solution file should always have valid SccProvider0 and SccProjectName0 values (have to be added manually with new versions of P4SCC plugins)
  • Project files should always have valid SccProjectName (preferrably same as SccProjectName0), SccLocalPath and SccProvider values (also have to be edited manually as the P4SCC defaults are no good)

I'm also including answers to your original questions:

What happens when you do "Change source control" and bind projects? How does Visual Studio decide what to put in the project and solution files?

This updates "Scc*" elements in a project file you're rebinding; the solution file is then updated as well so that it is in sync with the project file bindings

What happens when you do "Open from source control"?

Allows you to pick solution that you'd like to open. Afterwards all the projects included in the solution are automatically synced to head. I find this feature not very useful in Perforce world where you have to create a client anyway and the chances are you're syncing this client from a P4V/P4Win/P4 instead of relying on Visual Studio. This was kind-of useful in Visual Source Safe world where there was no concept of views and you were defining where a repository goes on checkout time.

What's this "connection" folder that SccLocalPath and SccProjectFilePathRelativizedFromConnection refer to? How does Visual Studio/Perforce pick it?

This is Visual Studio's bookkeeping. It is determined based on bindings in each project file (I guess in theory if a project file loses binding information for some reason, it could be reconstructed from the solution information...)

Is there some recommended way to make the source control bindings continue to work even when you create a new branch of the solution?

I hope the sections above give you some idea of a way that is working very well for me :-).

22
votes

Milan's post is well-researched and well-written, but its length demonstrates beyond a shadow of a doubt that the P4SCC model is broken. Storing source control binding info inside the project & solution files is ridiculous. Enforcing (via sccprojectname) that a project be part of only one solution is equally ridiculous.

Additionally, P4SCC has a tremendous performance cost in a large solution, as it retrieves info from source control for each file at startup, and maintains that state in memory throughout the development session. It creates extra cruft in the form of information-free .vsscc & vssscc files to support some SCC feature that (AFAICT) Perforce does not use.

The ideal Perforce integration looks like:

  • If I create a new solution, project, or project item, run 'p4 add'.
  • If I change a file, run 'p4 edit'.
  • Some toolbar/context menu integration for revision history, revision graph, timelapse/blame, and 'show in P4 gui'.
  • (nice to have) If I rename a file that exists in the depot, run 'p4 integrate' and 'p4 delete'. If I rename a file opened for add, run 'p4 revert' and 'p4 add'.
  • That's all

We have moved completely away from P4SCC and its bizarre requirements and burdens. Instead we use NiftyPerforce. There are some bugs, but we find working around these bugs to be much less frustrating than working around the design defects in the Perforce<->VSSCC model.

9
votes

Just to keep this current - the P4VS plugin has been rewritten circa 2012

Now you can perform all of your daily interaction with Perforce naturally, like checking in code and viewing file history, directly from the IDE.

If you’re a power user looking for a bit more, P4VS won’t disappoint. P4VS is fully compatible with Perforce Streams, and the Stream Graph is accessible from the IDE, along with Time-lapse View and Revision Graph. If you are responsible for branch management, you can merge from P4VS as well.

And, if you work remotely or want to do a little private branching, P4Sandbox can be configured through P4VS.

5
votes

Using Perforce with Visual Studio can be simplified by using the P4CONFIG environment variable.

Basically you go into Visual Studio, Tools -> Options -> Source Control -> Plug-in Settings, Advanced button. This will bring up a Perforce configuration dialog specific to the SCC integration. Switch to the Connection tab, and check the radio button titled 'Bind the workspace that matches your Perforce environment settings'. This will tell perforce to prefer using P4CONFIG environment variable for determining the environment you are under. This same dialog exists in P4V under Edit -> Preferences, but only affects p4v's behavior.

How you setup the P4CONFIG environment variable is up to you to some degree. I like having them be named the same everywhere so I set a system-wide environment variable P4CONFIG to look for a file named p4config.cfg. This file is just an ini style file, where you can assign other variables such as P4USER, P4CLIENT, P4HOST etc. Perforce will search for this file in the current directory and all parent directories until it encounters one. Basically you put this file in the root most directory of your where your clientspec is mapped to on your hard drive, and leave it alone.

This approach greatly reduces the amount of 'correctness' that the SCC configuration needs in visual studio in order to function. (SAK bindings work fine etc.)

If after syncing your code from perforce for the first time or to a totaly clean directory structure, and getting a dialog complaining about perforce wanting to either temporarily work offline or remove bindings, there is still some editing you need to do. Primarily the .sln file itself needs to be modified so it knows the sln has SCC bindings for itself . This is done by making sure the following fields are placed right after SccNumberOfProjects in the .sln file.

SccProjectName0 = Perforce\u0020Project
SccProvider0 = MSSCCI:Perforce\u0020SCM

All of the individual projects should work fine with default 'SAK bindings' provided you are using the P4CONFIG approach. Fixing this should allow Perforce to work from a clean sync perfectly, and also eliminate the generation of MSSCCPRJ.SCC cruft in each of the project directories.

4
votes

Support for renaming a file or moving it to a new folder directory is terrible and painful if using the Visual Studio P4 plug-in integration. No built-in feature exists that alerts P4 to renaming the file or that it has been moved.

The issue is that renaming a file requires not just updating the associated VS project file but Perforce needs to be informed as well of the change if you want to maintain proper revision history.

Currently, I do not see a way to do both in a single operation if using the VS integration. Instead, you have to:

  1. rename/move the file from within the Perforce client
  2. delete the old filename reference in the project file within VS
  3. add the new filename reference in the project file within VS
  4. submit your changes

If you use a continuous integration build process and you submit changes at any point prior to the last step, you are guaranteed to have a broken build.

The problem magnifies significantly the more files that require renaming or moving. This is not a smooth process whatsoever.

3
votes

After experimenting with Milan Gardian's very informative answer, I think I can provide a simpler solution to get it working pretty well.

As Weeble mentioned, the SAK values work fine when everything else is set up correctly, and they are often the default values (but I think it depends on which project type it is). If they do not show up in your project, just paste in this property group.

<PropertyGroup>
    <SccProjectName>SAK</SccProjectName>
    <SccProvider>SAK</SccProvider>
    <SccAuxPath>SAK</SccAuxPath>
    <SccLocalPath>SAK</SccLocalPath>
</PropertyGroup>

Add these two lines to the *.sln in the SourceCodeControl GlobalSection. As long as the project files have SAK values, they should inherit the names from the solution.

SccProjectName0 = Perforce\u0020Project
SccProvider0 = MSSCCI:Perforce\u0020SCM

No *.vssscc, *.vspscc, or *.SCC files need to be checked in (though they will be generated when the solution is opened).

2
votes

Very poorly. I know that is not the answer to your questions that you were looking for (in the future, perhaps you could narrow the focus?), but source control integration with Visual Studio just sucks. The reason being that they all have to use Microsoft's terrible SCC interface. It's pathetic! They put source control information in the project files! Why would they do that?

Just abandon the Visual Studio integration and use the Perforce client. It's not that much extra work. You can't spare 30 seconds per day to switch over to the Perforce client and check in/out the files out from there?

1
votes

I can answer the last one.

In order to get source control bindings to work even when you create a new branch, follow a strict hierarchical structure:

/Solution
  /library1
  /library2
  /product1
  /product2
  /subsolution
    /sublibrary1
    /subproduct1

Each file must be in exactly one .vcproj. You can have multiple .vcproj in the same directory, but if they share files, the shared files must go into their own .vcproj.

If you are relentless in this, all the Scc stuff will be relative-path, so a new branch will work (because it only changes the topmost directory).

-1
votes

This is not a Perforce issue, it is a Visual Studio issue. The ridiculous requirement that source files be modified to allow Visual Studio to understand that there is an SCM tool in use is idiotic.

The simple answer is 'stop using the Visual Studio source control integration'. It just plain sucks. Even with TFS it just plain sucks.

If you want to be able to check out from within Visual Studio, just create a custom tool. A simple call to 'p4 edit' with the appropriate VS variable is all you need. Want to revert a file? Same idea...call p4 revert with the appropriate variable. Done.

Use p4v to manage your source control needs (submissions, history, diffs, etc.) Visual Studio rocks as a source editor. It sucks as an SCM interface.