Exemple #1
0
        public static ConfigFile ReadProjectConfigFile(PerforceConnection Perforce, string BranchClientPath, string SelectedClientFileName, string CacheFolder, List <KeyValuePair <string, DateTime> > LocalConfigFiles, TextWriter Log)
        {
            List <string> ConfigFilePaths = Utility.GetDepotConfigPaths(BranchClientPath + "/Engine", SelectedClientFileName);

            ConfigFile ProjectConfig = new ConfigFile();

            List <PerforceFileRecord> FileRecords;

            if (Perforce.Stat("-Ol", ConfigFilePaths, out FileRecords, Log))
            {
                foreach (PerforceFileRecord FileRecord in FileRecords)
                {
                    List <string> Lines = null;

                    // Skip file records which are still in the workspace, but were synced from a different branch. For these files, the action seems to be empty, so filter against that.
                    if (FileRecord.Action == null)
                    {
                        continue;
                    }

                    // If this file is open for edit, read the local version
                    string LocalFileName = FileRecord.ClientPath;
                    if (LocalFileName != null && File.Exists(LocalFileName) && (File.GetAttributes(LocalFileName) & FileAttributes.ReadOnly) == 0)
                    {
                        try
                        {
                            DateTime LastModifiedTime = File.GetLastWriteTimeUtc(LocalFileName);
                            LocalConfigFiles.Add(new KeyValuePair <string, DateTime>(LocalFileName, LastModifiedTime));
                            Lines = File.ReadAllLines(LocalFileName).ToList();
                        }
                        catch (Exception Ex)
                        {
                            Log.WriteLine("Failed to read local config file for {0}: {1}", LocalFileName, Ex.ToString());
                        }
                    }

                    // Otherwise try to get it from perforce
                    if (Lines == null)
                    {
                        Utility.TryPrintFileUsingCache(Perforce, FileRecord.DepotPath, CacheFolder, FileRecord.Digest, out Lines, Log);
                    }

                    // Merge the text with the config file
                    if (Lines != null)
                    {
                        try
                        {
                            ProjectConfig.Parse(Lines.ToArray());
                            Log.WriteLine("Read config file from {0}", FileRecord.DepotPath);
                        }
                        catch (Exception Ex)
                        {
                            Log.WriteLine("Failed to read config file from {0}: {1}", FileRecord.DepotPath, Ex.ToString());
                        }
                    }
                }
            }
            return(ProjectConfig);
        }
        List <PerforceConnection> FilterClients(List <PerforceClientRecord> Clients, string ServerAndPort, string HostName, string UserName)
        {
            List <PerforceConnection> CandidateClients = new List <PerforceConnection>();

            foreach (PerforceClientRecord Client in Clients)
            {
                // Make sure the client is well formed
                if (!String.IsNullOrEmpty(Client.Name) && (!String.IsNullOrEmpty(Client.Host) || !String.IsNullOrEmpty(Client.Owner)) && !String.IsNullOrEmpty(Client.Root))
                {
                    // Require either a username or host name match
                    if ((String.IsNullOrEmpty(Client.Host) || String.Compare(Client.Host, HostName, StringComparison.InvariantCultureIgnoreCase) == 0) && (String.IsNullOrEmpty(Client.Owner) || String.Compare(Client.Owner, UserName, StringComparison.InvariantCultureIgnoreCase) == 0))
                    {
                        if (!Utility.SafeIsFileUnderDirectory(NewSelectedFileName, Client.Root))
                        {
                            Log.WriteLine("Rejecting {0} due to root mismatch ({1})", Client.Name, Client.Root);
                            continue;
                        }

                        PerforceConnection CandidateClient = new PerforceConnection(UserName, Client.Name, ServerAndPort);

                        bool bFileExists;
                        if (!CandidateClient.FileExists(NewSelectedFileName, out bFileExists, Log) || !bFileExists)
                        {
                            Log.WriteLine("Rejecting {0} due to file not existing in workspace", Client.Name);
                            continue;
                        }

                        List <PerforceFileRecord> Records;
                        if (!CandidateClient.Stat(NewSelectedFileName, out Records, Log))
                        {
                            Log.WriteLine("Rejecting {0} due to {1} not in depot", Client.Name, NewSelectedFileName);
                            continue;
                        }

                        Records.RemoveAll(x => !x.IsMapped);
                        if (Records.Count == 0)
                        {
                            Log.WriteLine("Rejecting {0} due to {1} matching records", Client.Name, Records.Count);
                            continue;
                        }

                        Log.WriteLine("Found valid client {0}", Client.Name);
                        CandidateClients.Add(CandidateClient);
                    }
                }
            }
            return(CandidateClients);
        }
        public bool Run(PerforceConnection Perforce, TextWriter Log, out string ErrorMessage)
        {
            // Use the cached client path to the file if it's available; it's much quicker than trying to find the correct workspace.
            if (!String.IsNullOrEmpty(SelectedProject.ClientPath))
            {
                // Get the client path
                NewSelectedClientFileName = SelectedProject.ClientPath;

                // Get the client name
                string ClientName;
                if (!PerforceUtils.TryGetClientName(NewSelectedClientFileName, out ClientName))
                {
                    ErrorMessage = String.Format("Couldn't get client name from {0}", NewSelectedClientFileName);
                    return(false);
                }

                // Create the client
                PerforceClient = new PerforceConnection(Perforce.UserName, ClientName, Perforce.ServerAndPort);

                // Figure out the path on the client. Use the cached location if it's valid.
                if (SelectedProject.LocalPath != null && File.Exists(SelectedProject.LocalPath))
                {
                    NewSelectedFileName = SelectedProject.LocalPath;
                }
                else
                {
                    if (!PerforceClient.ConvertToLocalPath(NewSelectedClientFileName, out NewSelectedFileName, Log))
                    {
                        ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedClientFileName);
                        return(false);
                    }
                }
            }
            else
            {
                // Get the perforce server settings
                PerforceInfoRecord PerforceInfo;
                if (!Perforce.Info(out PerforceInfo, Log))
                {
                    ErrorMessage = String.Format("Couldn't get Perforce server info");
                    return(false);
                }

                // Use the path as the selected filename
                NewSelectedFileName = SelectedProject.LocalPath;

                // Make sure the project exists
                if (!File.Exists(SelectedProject.LocalPath))
                {
                    ErrorMessage = String.Format("{0} does not exist.", SelectedProject.LocalPath);
                    return(false);
                }

                // Find all the clients for this user
                Log.WriteLine("Enumerating clients for {0}...", PerforceInfo.UserName);

                List <PerforceClientRecord> Clients;
                if (!Perforce.FindClients(PerforceInfo.UserName, out Clients, Log))
                {
                    ErrorMessage = String.Format("Couldn't find any clients for this host.");
                    return(false);
                }

                List <PerforceConnection> CandidateClients = FilterClients(Clients, Perforce.ServerAndPort, PerforceInfo.HostName, PerforceInfo.UserName);
                if (CandidateClients.Count == 0)
                {
                    // Search through all workspaces. We may find a suitable workspace which is for any user.
                    Log.WriteLine("Enumerating shared clients...");
                    if (!Perforce.FindClients("", out Clients, Log))
                    {
                        ErrorMessage = "Failed to enumerate clients.";
                        return(false);
                    }

                    // Filter this list of clients
                    CandidateClients = FilterClients(Clients, Perforce.ServerAndPort, PerforceInfo.HostName, PerforceInfo.UserName);

                    // If we still couldn't find any, fail.
                    if (CandidateClients.Count == 0)
                    {
                        ErrorMessage = String.Format("Couldn't find any Perforce workspace containing {0}. Check your connection settings.", NewSelectedFileName);
                        return(false);
                    }
                }

                // Check there's only one client
                if (CandidateClients.Count > 1)
                {
                    ErrorMessage = String.Format("Found multiple workspaces containing {0}:\n\n{1}\n\nCannot determine which to use.", Path.GetFileName(NewSelectedFileName), String.Join("\n", CandidateClients.Select(x => x.ClientName)));
                    return(false);
                }

                // Take the client we've chosen
                PerforceClient = CandidateClients[0];

                // Get the client path for the project file
                if (!PerforceClient.ConvertToClientPath(NewSelectedFileName, out NewSelectedClientFileName, Log))
                {
                    ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName);
                    return(false);
                }
            }

            // Normalize the filename
            NewSelectedFileName = Path.GetFullPath(NewSelectedFileName).Replace('/', Path.DirectorySeparatorChar);

            // Make sure the path case is correct. This can cause UBT intermediates to be out of date if the case mismatches.
            NewSelectedFileName = Utility.GetPathWithCorrectCase(new FileInfo(NewSelectedFileName));

            // Update the selected project with all the data we've found
            SelectedProject = new UserSelectedProjectSettings(SelectedProject.ServerAndPort, SelectedProject.UserName, SelectedProject.Type, NewSelectedClientFileName, NewSelectedFileName);

            // Figure out where the engine is in relation to it
            int EndIdx = NewSelectedClientFileName.Length - 1;

            if (EndIdx != -1 && NewSelectedClientFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                EndIdx = NewSelectedClientFileName.LastIndexOf('/') - 1;
            }
            for (;; EndIdx--)
            {
                if (EndIdx < 2)
                {
                    ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
                    return(false);
                }
                if (NewSelectedClientFileName[EndIdx] == '/')
                {
                    List <PerforceFileRecord> FileRecords;
                    if (PerforceClient.Stat(NewSelectedClientFileName.Substring(0, EndIdx) + "/Engine/Build/Build.version", out FileRecords, Log) && FileRecords.Count > 0)
                    {
                        if (FileRecords[0].ClientPath == null)
                        {
                            ErrorMessage = String.Format("Missing client path for {0}", FileRecords[0].DepotPath);
                            return(false);
                        }

                        BranchClientPath    = NewSelectedClientFileName.Substring(0, EndIdx);
                        BranchDirectoryName = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(FileRecords[0].ClientPath), "..", ".."));
                        break;
                    }
                }
            }
            Log.WriteLine("Found branch root at {0}", BranchClientPath);

            // Find the editor target for this project
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                List <PerforceFileRecord> Files;
                if (PerforceClient.FindFiles(PerforceUtils.GetClientOrDepotDirectoryName(NewSelectedClientFileName) + "/Source/*Editor.Target.cs", out Files, Log) && Files.Count >= 1)
                {
                    string DepotPath = Files[0].DepotPath;
                    NewProjectEditorTarget = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DepotPath.Substring(DepotPath.LastIndexOf('/') + 1)));
                    Log.WriteLine("Using {0} as editor target name (from {1})", NewProjectEditorTarget, Files[0]);
                }
                if (NewProjectEditorTarget == null)
                {
                    Log.WriteLine("Couldn't find any editor targets for this project.");
                }
            }

            // Get a unique name for the project that's selected. For regular branches, this can be the depot path. For streams, we want to include the stream name to encode imports.
            if (PerforceClient.GetActiveStream(out StreamName, Log))
            {
                string ExpectedPrefix = String.Format("//{0}/", PerforceClient.ClientName);
                if (!NewSelectedClientFileName.StartsWith(ExpectedPrefix, StringComparison.InvariantCultureIgnoreCase))
                {
                    ErrorMessage = String.Format("Unexpected client path; expected '{0}' to begin with '{1}'", NewSelectedClientFileName, ExpectedPrefix);
                    return(false);
                }
                string StreamPrefix;
                if (!TryGetStreamPrefix(PerforceClient, StreamName, Log, out StreamPrefix))
                {
                    ErrorMessage = String.Format("Failed to get stream info for {0}", StreamName);
                    return(false);
                }
                NewSelectedProjectIdentifier = String.Format("{0}/{1}", StreamPrefix, NewSelectedClientFileName.Substring(ExpectedPrefix.Length));
            }
            else
            {
                if (!PerforceClient.ConvertToDepotPath(NewSelectedClientFileName, out NewSelectedProjectIdentifier, Log))
                {
                    ErrorMessage = String.Format("Couldn't get depot path for {0}", NewSelectedFileName);
                    return(false);
                }
            }

            // Read the project logo
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                string LogoFileName = Path.Combine(Path.GetDirectoryName(NewSelectedFileName), "Build", "UnrealGameSync.png");
                if (File.Exists(LogoFileName))
                {
                    try
                    {
                        // Duplicate the image, otherwise we'll leave the file locked
                        using (Image Image = Image.FromFile(LogoFileName))
                        {
                            ProjectLogo = new Bitmap(Image);
                        }
                    }
                    catch
                    {
                        ProjectLogo = null;
                    }
                }
            }

            // Figure out if it's an enterprise project. Use the synced version if we have it.
            if (NewSelectedClientFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                string Text;
                if (File.Exists(NewSelectedFileName))
                {
                    Text = File.ReadAllText(NewSelectedFileName);
                }
                else
                {
                    List <string> ProjectLines;
                    if (!PerforceClient.Print(NewSelectedClientFileName, out ProjectLines, Log))
                    {
                        ErrorMessage = String.Format("Unable to get contents of {0}", NewSelectedClientFileName);
                        return(false);
                    }
                    Text = String.Join("\n", ProjectLines);
                }
                bIsEnterpriseProject = Utility.IsEnterpriseProjectFromText(Text);
            }

            // Read the initial config file
            LocalConfigFiles        = new List <KeyValuePair <string, DateTime> >();
            LatestProjectConfigFile = PerforceMonitor.ReadProjectConfigFile(PerforceClient, BranchClientPath, NewSelectedClientFileName, CacheFolder, LocalConfigFiles, Log);

            // Succeed!
            ErrorMessage = null;
            return(true);
        }
        public bool Run(out string ErrorMessage)
        {
            Log.WriteLine("Finding files in workspace...");
            Log.WriteLine();

            // Start enumerating all the files that exist locally
            foreach (string SyncPath in SyncPaths)
            {
                Debug.Assert(SyncPath.StartsWith(ClientRootPath));
                if (SyncPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    string[] Fragments = SyncPath.Substring(ClientRootPath.Length).Split('/');

                    FolderToClean SyncFolder = RootFolderToClean;
                    for (int Idx = 0; Idx < Fragments.Length - 1; Idx++)
                    {
                        FolderToClean NextSyncFolder;
                        if (!SyncFolder.NameToSubFolder.TryGetValue(Fragments[Idx], out NextSyncFolder))
                        {
                            NextSyncFolder = new FolderToClean(new DirectoryInfo(Path.Combine(SyncFolder.Directory.FullName, Fragments[Idx])));
                            SyncFolder.NameToSubFolder[NextSyncFolder.Name] = NextSyncFolder;
                        }
                        SyncFolder = NextSyncFolder;
                    }

                    string Wildcard = Fragments[Fragments.Length - 1];
                    if (Wildcard == "...")
                    {
                        QueueFolderToPopulate(SyncFolder);
                    }
                    else
                    {
                        if (SyncFolder.Directory.Exists)
                        {
                            foreach (FileInfo File in SyncFolder.Directory.EnumerateFiles(Wildcard))
                            {
                                SyncFolder.NameToFile[File.Name] = File;
                            }
                        }
                    }
                }
            }

            // Get the prefix for any local file
            string LocalRootPrefix = RootFolderToClean.Directory.FullName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;

            // Query the have table and build a separate tree from it
            PerforceHaveFolder RootHaveFolder = new PerforceHaveFolder();

            foreach (string SyncPath in SyncPaths)
            {
                List <PerforceFileRecord> FileRecords;
                if (!PerforceClient.Stat(String.Format("{0}#have", SyncPath), out FileRecords, Log))
                {
                    ErrorMessage = "Couldn't query have table from Perforce.";
                    return(false);
                }
                foreach (PerforceFileRecord FileRecord in FileRecords)
                {
                    if (!FileRecord.ClientPath.StartsWith(LocalRootPrefix, StringComparison.InvariantCultureIgnoreCase))
                    {
                        ErrorMessage = String.Format("Failed to get have table; file '{0}' doesn't start with root path ('{1}')", FileRecord.ClientPath, RootFolderToClean.Directory.FullName);
                        return(false);
                    }

                    string[] Tokens = FileRecord.ClientPath.Substring(LocalRootPrefix.Length).Split('/', '\\');

                    PerforceHaveFolder FileFolder = RootHaveFolder;
                    for (int Idx = 0; Idx < Tokens.Length - 1; Idx++)
                    {
                        PerforceHaveFolder NextFileFolder;
                        if (!FileFolder.NameToSubFolder.TryGetValue(Tokens[Idx], out NextFileFolder))
                        {
                            NextFileFolder = new PerforceHaveFolder();
                            FileFolder.NameToSubFolder.Add(Tokens[Idx], NextFileFolder);
                        }
                        FileFolder = NextFileFolder;
                    }
                    FileFolder.NameToFile[Tokens[Tokens.Length - 1]] = FileRecord;
                }
            }

            // Find all the files which are currently open for edit. We don't want to force sync these.
            List <PerforceFileRecord> OpenFileRecords;

            if (!PerforceClient.GetOpenFiles("//...", out OpenFileRecords, Log))
            {
                ErrorMessage = "Couldn't query open files from Perforce.";
                return(false);
            }

            // Build a set of all the open local files
            HashSet <string> OpenLocalFiles = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);

            foreach (PerforceFileRecord OpenFileRecord in OpenFileRecords)
            {
                if (!OpenFileRecord.ClientPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    ErrorMessage = String.Format("Failed to get open file list; file '{0}' doesn't start with client root path ('{1}')", OpenFileRecord.ClientPath, ClientRootPath);
                    return(false);
                }
                OpenLocalFiles.Add(LocalRootPrefix + PerforceUtils.UnescapePath(OpenFileRecord.ClientPath).Substring(ClientRootPath.Length).Replace('/', Path.DirectorySeparatorChar));
            }

            // Wait to finish scanning the directory
            FinishedScan.WaitOne();

            // Find the value of the P4CONFIG variable
            string PerforceConfigFile;

            PerforceClient.GetSetting("P4CONFIG", out PerforceConfigFile, Log);

            // Merge the trees
            MergeTrees(RootFolderToClean, RootHaveFolder, OpenLocalFiles, PerforceConfigFile);

            // Remove all the empty folders
            RemoveEmptyFolders(RootFolderToClean);
            ErrorMessage = null;
            return(true);
        }
Exemple #5
0
        public bool Run(out string ErrorMessage)
        {
            PerforceConnection Perforce = new PerforceConnection(null, null, null);

            // Get the Perforce server info
            PerforceInfoRecord PerforceInfo;

            if (!Perforce.Info(out PerforceInfo, Log))
            {
                ErrorMessage = String.Format("Couldn't get Perforce server info");
                return(false);
            }
            if (String.IsNullOrEmpty(PerforceInfo.UserName))
            {
                ErrorMessage = "Missing user name in call to p4 info";
                return(false);
            }
            if (String.IsNullOrEmpty(PerforceInfo.HostName))
            {
                ErrorMessage = "Missing host name in call to p4 info";
                return(false);
            }
            ServerTimeZone = PerforceInfo.ServerTimeZone;

            // Find all the clients on this machine
            Log.WriteLine("Enumerating clients on local machine...");
            List <PerforceClientRecord> Clients;

            if (!Perforce.FindClients(out Clients, Log))
            {
                ErrorMessage = String.Format("Couldn't find any clients for this host.");
                return(false);
            }

            // Find any clients which are valid. If this is not exactly one, we should fail.
            List <PerforceConnection> CandidateClients = new List <PerforceConnection>();

            foreach (PerforceClientRecord Client in Clients)
            {
                // Make sure the client is well formed
                if (!String.IsNullOrEmpty(Client.Name) && (!String.IsNullOrEmpty(Client.Host) || !String.IsNullOrEmpty(Client.Owner)) && !String.IsNullOrEmpty(Client.Root))
                {
                    // Require either a username or host name match
                    if ((String.IsNullOrEmpty(Client.Host) || String.Compare(Client.Host, PerforceInfo.HostName, StringComparison.InvariantCultureIgnoreCase) == 0) && (String.IsNullOrEmpty(Client.Owner) || String.Compare(Client.Owner, PerforceInfo.UserName, StringComparison.InvariantCultureIgnoreCase) == 0))
                    {
                        if (!Utility.SafeIsFileUnderDirectory(NewSelectedFileName, Client.Root))
                        {
                            Log.WriteLine("Rejecting {0} due to root mismatch ({1})", Client.Name, Client.Root);
                            continue;
                        }

                        PerforceConnection CandidateClient = new PerforceConnection(PerforceInfo.UserName, Client.Name, Perforce.ServerAndPort);

                        bool bFileExists;
                        if (!CandidateClient.FileExists(NewSelectedFileName, out bFileExists, Log) || !bFileExists)
                        {
                            Log.WriteLine("Rejecting {0} due to file not existing in workspace", Client.Name);
                            continue;
                        }

                        List <PerforceFileRecord> Records;
                        if (!CandidateClient.Stat(NewSelectedFileName, out Records, Log))
                        {
                            Log.WriteLine("Rejecting {0} due to {1} not in depot", Client.Name, NewSelectedFileName);
                            continue;
                        }

                        Records.RemoveAll(x => !x.IsMapped);
                        if (Records.Count != 1)
                        {
                            Log.WriteLine("Rejecting {0} due to {1} matching records", Client.Name, Records.Count);
                            continue;
                        }

                        Log.WriteLine("Found valid client {0}", Client.Name);
                        CandidateClients.Add(CandidateClient);
                    }
                }
            }

            // Check there's only one client
            if (CandidateClients.Count == 0)
            {
                ErrorMessage = String.Format("Couldn't find any Perforce workspace containing {0}.", NewSelectedFileName);
                return(false);
            }
            else if (CandidateClients.Count > 1)
            {
                ErrorMessage = String.Format("Found multiple workspaces containing {0}:\n\n{1}\n\nCannot determine which to use.", Path.GetFileName(NewSelectedFileName), String.Join("\n", CandidateClients.Select(x => x.ClientName)));
                return(false);
            }

            // Take the client we've chosen
            PerforceClient = CandidateClients[0];

            // Get the client path for the project file
            if (!PerforceClient.ConvertToClientPath(NewSelectedFileName, out NewSelectedClientFileName, Log))
            {
                ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName);
                return(false);
            }

            // Figure out where the engine is in relation to it
            for (int EndIdx = NewSelectedClientFileName.Length - 1;; EndIdx--)
            {
                if (EndIdx < 2)
                {
                    ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
                    return(false);
                }
                if (NewSelectedClientFileName[EndIdx] == '/')
                {
                    bool bFileExists;
                    if (!PerforceClient.FileExists(NewSelectedClientFileName.Substring(0, EndIdx) + "/Engine/Source/UE4Editor.target.cs", out bFileExists, Log))
                    {
                        ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
                        return(false);
                    }
                    else if (bFileExists)
                    {
                        BranchClientPath = NewSelectedClientFileName.Substring(0, EndIdx);
                        break;
                    }
                }
            }
            Log.WriteLine("Found branch root at {0}", BranchClientPath);

            // Get the local branch root
            if (!PerforceClient.ConvertToLocalPath(BranchClientPath + "/Engine/Source/UE4Editor.target.cs", out BaseEditorTargetPath, Log))
            {
                ErrorMessage = String.Format("Couldn't get local path for editor target file");
                return(false);
            }

            // Find the editor target for this project
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                List <PerforceFileRecord> Files;
                if (PerforceClient.FindFiles(PerforceUtils.GetClientOrDepotDirectoryName(NewSelectedClientFileName) + "/Source/*Editor.Target.cs", out Files, Log) && Files.Count >= 1)
                {
                    PerforceFileRecord File = Files.FirstOrDefault(x => x.Action == null || !x.Action.Contains("delete"));
                    if (File == null)
                    {
                        Log.WriteLine("Couldn't find any non-deleted editor targets for this project.");
                    }
                    else
                    {
                        string DepotPath = File.DepotPath;
                        NewProjectEditorTarget = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DepotPath.Substring(DepotPath.LastIndexOf('/') + 1)));
                        Log.WriteLine("Using {0} as editor target name (from {1})", NewProjectEditorTarget, Files[0]);
                    }
                }
                else
                {
                    Log.WriteLine("Couldn't find any editor targets for this project.");
                }
            }

            // Get a unique name for the project that's selected. For regular branches, this can be the depot path. For streams, we want to include the stream name to encode imports.
            if (PerforceClient.GetActiveStream(out StreamName, Log))
            {
                string ExpectedPrefix = String.Format("//{0}/", PerforceClient.ClientName);
                if (!NewSelectedClientFileName.StartsWith(ExpectedPrefix, StringComparison.InvariantCultureIgnoreCase))
                {
                    ErrorMessage = String.Format("Unexpected client path; expected '{0}' to begin with '{1}'", NewSelectedClientFileName, ExpectedPrefix);
                    return(false);
                }
                string StreamPrefix;
                if (!TryGetStreamPrefix(PerforceClient, StreamName, Log, out StreamPrefix))
                {
                    ErrorMessage = String.Format("Failed to get stream info for {0}", StreamName);
                    return(false);
                }
                NewSelectedProjectIdentifier = String.Format("{0}/{1}", StreamPrefix, NewSelectedClientFileName.Substring(ExpectedPrefix.Length));
            }
            else
            {
                if (!PerforceClient.ConvertToDepotPath(NewSelectedClientFileName, out NewSelectedProjectIdentifier, Log))
                {
                    ErrorMessage = String.Format("Couldn't get depot path for {0}", NewSelectedFileName);
                    return(false);
                }
            }

            // Read the project logo
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                string LogoFileName = Path.Combine(Path.GetDirectoryName(NewSelectedFileName), "Build", "UnrealGameSync.png");
                if (File.Exists(LogoFileName))
                {
                    try
                    {
                        // Duplicate the image, otherwise we'll leave the file locked
                        using (Image Image = Image.FromFile(LogoFileName))
                        {
                            ProjectLogo = new Bitmap(Image);
                        }
                    }
                    catch
                    {
                        ProjectLogo = null;
                    }
                }
            }

            // Succeeed!
            ErrorMessage = null;
            return(true);
        }
        public bool Run(PerforceConnection Perforce, TextWriter Log, out string ErrorMessage)
        {
            // Get the perforce server settings
            PerforceInfoRecord PerforceInfo;

            if (!Perforce.Info(out PerforceInfo, Log))
            {
                ErrorMessage = String.Format("Couldn't get Perforce server info");
                return(false);
            }

            // Configure the time zone
            ServerTimeZone = PerforceInfo.ServerTimeZone;

            // If we're using the legacy path of specifying a file, figure out the workspace name now
            if (SelectedProject.Type == UserSelectedProjectType.Client)
            {
                // Get the client path
                NewSelectedClientFileName = SelectedProject.ClientPath;

                // Get the client name
                string ClientName;
                if (!PerforceUtils.TryGetClientName(NewSelectedClientFileName, out ClientName))
                {
                    ErrorMessage = String.Format("Couldn't get client name from {0}", NewSelectedClientFileName);
                    return(false);
                }

                // Create the client
                PerforceClient = new PerforceConnection(Perforce.UserName, ClientName, Perforce.ServerAndPort);

                // Figure out the path on the client
                if (!PerforceClient.ConvertToLocalPath(NewSelectedClientFileName, out NewSelectedFileName, Log))
                {
                    ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName);
                    return(false);
                }
            }
            else if (SelectedProject.Type == UserSelectedProjectType.Local)
            {
                // Use the path as the selected filename
                NewSelectedFileName = SelectedProject.LocalPath;

                // Make sure the project exists
                if (!File.Exists(SelectedProject.LocalPath))
                {
                    ErrorMessage = String.Format("{0} does not exist.", SelectedProject.LocalPath);
                    return(false);
                }

                // Find all the clients on this machine
                Log.WriteLine("Enumerating clients on local machine...");
                List <PerforceClientRecord> Clients;
                if (!Perforce.FindClients(out Clients, Log))
                {
                    ErrorMessage = String.Format("Couldn't find any clients for this host.");
                    return(false);
                }

                // Find any clients which are valid. If this is not exactly one, we should fail.
                List <PerforceConnection> CandidateClients = new List <PerforceConnection>();
                foreach (PerforceClientRecord Client in Clients)
                {
                    // Make sure the client is well formed
                    if (!String.IsNullOrEmpty(Client.Name) && (!String.IsNullOrEmpty(Client.Host) || !String.IsNullOrEmpty(Client.Owner)) && !String.IsNullOrEmpty(Client.Root))
                    {
                        // Require either a username or host name match
                        if ((String.IsNullOrEmpty(Client.Host) || String.Compare(Client.Host, PerforceInfo.HostName, StringComparison.InvariantCultureIgnoreCase) == 0) && (String.IsNullOrEmpty(Client.Owner) || String.Compare(Client.Owner, PerforceInfo.UserName, StringComparison.InvariantCultureIgnoreCase) == 0))
                        {
                            if (!Utility.SafeIsFileUnderDirectory(NewSelectedFileName, Client.Root))
                            {
                                Log.WriteLine("Rejecting {0} due to root mismatch ({1})", Client.Name, Client.Root);
                                continue;
                            }

                            PerforceConnection CandidateClient = new PerforceConnection(PerforceInfo.UserName, Client.Name, Perforce.ServerAndPort);

                            bool bFileExists;
                            if (!CandidateClient.FileExists(NewSelectedFileName, out bFileExists, Log) || !bFileExists)
                            {
                                Log.WriteLine("Rejecting {0} due to file not existing in workspace", Client.Name);
                                continue;
                            }

                            List <PerforceFileRecord> Records;
                            if (!CandidateClient.Stat(NewSelectedFileName, out Records, Log))
                            {
                                Log.WriteLine("Rejecting {0} due to {1} not in depot", Client.Name, NewSelectedFileName);
                                continue;
                            }

                            Records.RemoveAll(x => !x.IsMapped);
                            if (Records.Count == 0)
                            {
                                Log.WriteLine("Rejecting {0} due to {1} matching records", Client.Name, Records.Count);
                                continue;
                            }

                            Log.WriteLine("Found valid client {0}", Client.Name);
                            CandidateClients.Add(CandidateClient);
                        }
                    }
                }

                // Check there's only one client
                if (CandidateClients.Count == 0)
                {
                    ErrorMessage = String.Format("Couldn't find any Perforce workspace containing {0}. Check your connection settings.", NewSelectedFileName);
                    return(false);
                }
                else if (CandidateClients.Count > 1)
                {
                    ErrorMessage = String.Format("Found multiple workspaces containing {0}:\n\n{1}\n\nCannot determine which to use.", Path.GetFileName(NewSelectedFileName), String.Join("\n", CandidateClients.Select(x => x.ClientName)));
                    return(false);
                }

                // Take the client we've chosen
                PerforceClient = CandidateClients[0];

                // Get the client path for the project file
                if (!PerforceClient.ConvertToClientPath(NewSelectedFileName, out NewSelectedClientFileName, Log))
                {
                    ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName);
                    return(false);
                }
            }
            else
            {
                throw new InvalidDataException("Invalid selected project type");
            }

            // Normalize the filename
            NewSelectedFileName = Path.GetFullPath(NewSelectedFileName).Replace('/', Path.DirectorySeparatorChar);

            // Make sure the path case is correct. This can cause UBT intermediates to be out of date if the case mismatches.
            NewSelectedFileName = Utility.GetPathWithCorrectCase(new FileInfo(NewSelectedFileName));

            // Update the selected project with all the data we've found
            SelectedProject = new UserSelectedProjectSettings(Perforce.ServerAndPort, Perforce.UserName, SelectedProject.Type, NewSelectedClientFileName, NewSelectedFileName);

            // Figure out where the engine is in relation to it
            for (int EndIdx = NewSelectedClientFileName.Length - 1;; EndIdx--)
            {
                if (EndIdx < 2)
                {
                    ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
                    return(false);
                }
                if (NewSelectedClientFileName[EndIdx] == '/')
                {
                    bool bFileExists;
                    if (PerforceClient.FileExists(NewSelectedClientFileName.Substring(0, EndIdx) + "/Engine/Build/Build.version", out bFileExists, Log) && bFileExists)
                    {
                        BranchClientPath = NewSelectedClientFileName.Substring(0, EndIdx);
                        break;
                    }
                }
            }
            Log.WriteLine("Found branch root at {0}", BranchClientPath);

            // Get the local branch root
            string BuildVersionPath;

            if (!PerforceClient.ConvertToLocalPath(BranchClientPath + "/Engine/Build/Build.version", out BuildVersionPath, Log))
            {
                ErrorMessage = String.Format("Couldn't get local path for Engine/Build/Build.version");
                return(false);
            }
            BranchDirectoryName = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(BuildVersionPath), "..", ".."));

            // Find the editor target for this project
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                List <PerforceFileRecord> Files;
                if (PerforceClient.FindFiles(PerforceUtils.GetClientOrDepotDirectoryName(NewSelectedClientFileName) + "/Source/*Editor.Target.cs", out Files, Log) && Files.Count >= 1)
                {
                    PerforceFileRecord File = Files.FirstOrDefault(x => x.Action == null || !x.Action.Contains("delete"));
                    if (File != null)
                    {
                        string DepotPath = File.DepotPath;
                        NewProjectEditorTarget = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DepotPath.Substring(DepotPath.LastIndexOf('/') + 1)));
                        Log.WriteLine("Using {0} as editor target name (from {1})", NewProjectEditorTarget, Files[0]);
                    }
                }
                if (NewProjectEditorTarget == null)
                {
                    Log.WriteLine("Couldn't find any editor targets for this project.");
                }
            }

            // Get a unique name for the project that's selected. For regular branches, this can be the depot path. For streams, we want to include the stream name to encode imports.
            if (PerforceClient.GetActiveStream(out StreamName, Log))
            {
                string ExpectedPrefix = String.Format("//{0}/", PerforceClient.ClientName);
                if (!NewSelectedClientFileName.StartsWith(ExpectedPrefix, StringComparison.InvariantCultureIgnoreCase))
                {
                    ErrorMessage = String.Format("Unexpected client path; expected '{0}' to begin with '{1}'", NewSelectedClientFileName, ExpectedPrefix);
                    return(false);
                }
                string StreamPrefix;
                if (!TryGetStreamPrefix(PerforceClient, StreamName, Log, out StreamPrefix))
                {
                    ErrorMessage = String.Format("Failed to get stream info for {0}", StreamName);
                    return(false);
                }
                NewSelectedProjectIdentifier = String.Format("{0}/{1}", StreamPrefix, NewSelectedClientFileName.Substring(ExpectedPrefix.Length));
            }
            else
            {
                if (!PerforceClient.ConvertToDepotPath(NewSelectedClientFileName, out NewSelectedProjectIdentifier, Log))
                {
                    ErrorMessage = String.Format("Couldn't get depot path for {0}", NewSelectedFileName);
                    return(false);
                }
            }

            // Read the project logo
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                string LogoFileName = Path.Combine(Path.GetDirectoryName(NewSelectedFileName), "Build", "UnrealGameSync.png");
                if (File.Exists(LogoFileName))
                {
                    try
                    {
                        // Duplicate the image, otherwise we'll leave the file locked
                        using (Image Image = Image.FromFile(LogoFileName))
                        {
                            ProjectLogo = new Bitmap(Image);
                        }
                    }
                    catch
                    {
                        ProjectLogo = null;
                    }
                }
            }

            // Figure out if it's an enterprise project
            List <string> ProjectLines;

            if (NewSelectedClientFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase) && PerforceClient.Print(NewSelectedClientFileName, out ProjectLines, Log))
            {
                string Text = String.Join("\n", ProjectLines);
                bIsEnterpriseProject = Utility.IsEnterpriseProjectFromText(Text);
            }

            // Read the initial config file
            LocalConfigFiles        = new List <KeyValuePair <string, DateTime> >();
            LatestProjectConfigFile = PerforceMonitor.ReadProjectConfigFile(PerforceClient, BranchClientPath, NewSelectedClientFileName, LocalConfigFiles, Log);

            // Succeed!
            ErrorMessage = null;
            return(true);
        }
		public bool Run(out string ErrorMessage)
		{
			PerforceConnection Perforce = new PerforceConnection(null, null, null);

			// Get the Perforce server info
			PerforceInfoRecord PerforceInfo;
			if(!Perforce.Info(out PerforceInfo, Log))
			{
				ErrorMessage = String.Format("Couldn't get Perforce server info");
				return false;
			}
			if(String.IsNullOrEmpty(PerforceInfo.UserName))
			{
				ErrorMessage = "Missing user name in call to p4 info";
				return false;
			}
			if(String.IsNullOrEmpty(PerforceInfo.HostName))
			{
				ErrorMessage = "Missing host name in call to p4 info";
				return false;
			}
			ServerTimeZone = PerforceInfo.ServerTimeZone;

			// Find all the clients on this machine
			Log.WriteLine("Enumerating clients on local machine...");
			List<PerforceClientRecord> Clients;
			if(!Perforce.FindClients(out Clients, Log))
			{
				ErrorMessage = String.Format("Couldn't find any clients for this host.");
				return false;
			}

			// Find any clients which are valid. If this is not exactly one, we should fail.
			List<PerforceConnection> CandidateClients = new List<PerforceConnection>();
			foreach(PerforceClientRecord Client in Clients)
			{
				// Make sure the client is well formed
				if(!String.IsNullOrEmpty(Client.Name) && (!String.IsNullOrEmpty(Client.Host) || !String.IsNullOrEmpty(Client.Owner)) && !String.IsNullOrEmpty(Client.Root))
				{
					// Require either a username or host name match
					if((String.IsNullOrEmpty(Client.Host) || String.Compare(Client.Host, PerforceInfo.HostName, StringComparison.InvariantCultureIgnoreCase) == 0) && (String.IsNullOrEmpty(Client.Owner) || String.Compare(Client.Owner, PerforceInfo.UserName, StringComparison.InvariantCultureIgnoreCase) == 0))
					{
						if(!Utility.SafeIsFileUnderDirectory(NewSelectedFileName, Client.Root))
						{
							Log.WriteLine("Rejecting {0} due to root mismatch ({1})", Client.Name, Client.Root);
							continue;
						}

						PerforceConnection CandidateClient = new PerforceConnection(PerforceInfo.UserName, Client.Name, Perforce.ServerAndPort);

						bool bFileExists;
						if(!CandidateClient.FileExists(NewSelectedFileName, out bFileExists, Log) || !bFileExists)
						{
							Log.WriteLine("Rejecting {0} due to file not existing in workspace", Client.Name);
							continue;
						}

						List<PerforceFileRecord> Records;
						if(!CandidateClient.Stat(NewSelectedFileName, out Records, Log))
						{
							Log.WriteLine("Rejecting {0} due to {1} not in depot", Client.Name, NewSelectedFileName);
							continue;
						}

						Records.RemoveAll(x => !x.IsMapped);
						if(Records.Count != 1)
						{
							Log.WriteLine("Rejecting {0} due to {1} matching records", Client.Name, Records.Count);
							continue;
						}

						Log.WriteLine("Found valid client {0}", Client.Name);
						CandidateClients.Add(CandidateClient);
					}
				}
			}

			// Check there's only one client
			if(CandidateClients.Count == 0)
			{
				ErrorMessage = String.Format("Couldn't find any Perforce workspace containing {0}.", NewSelectedFileName);
				return false;
			}
			else if(CandidateClients.Count > 1)
			{
				ErrorMessage = String.Format("Found multiple workspaces containing {0}:\n\n{1}\n\nCannot determine which to use.", Path.GetFileName(NewSelectedFileName), String.Join("\n", CandidateClients.Select(x => x.ClientName)));
				return false;
			}

			// Take the client we've chosen
			PerforceClient = CandidateClients[0];

			// Get the client path for the project file
			if(!PerforceClient.ConvertToClientPath(NewSelectedFileName, out NewSelectedClientFileName, Log))
			{
				ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName);
				return false;
			}

			// Figure out where the engine is in relation to it
			for(int EndIdx = NewSelectedClientFileName.Length - 1;;EndIdx--)
			{
				if(EndIdx < 2)
				{
					ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
					return false;
				}
				if(NewSelectedClientFileName[EndIdx] == '/')
				{
					bool bFileExists;
					if(!PerforceClient.FileExists(NewSelectedClientFileName.Substring(0, EndIdx) + "/Engine/Source/UE4Editor.target.cs", out bFileExists, Log))
					{
						ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
						return false;
					}
					else if(bFileExists)
					{
						BranchClientPath = NewSelectedClientFileName.Substring(0, EndIdx);
						break;
					}
				}
			}
			Log.WriteLine("Found branch root at {0}", BranchClientPath);

			// Get the local branch root
			if(!PerforceClient.ConvertToLocalPath(BranchClientPath + "/Engine/Source/UE4Editor.target.cs", out BaseEditorTargetPath, Log))
			{
				ErrorMessage = String.Format("Couldn't get local path for editor target file");
				return false;
			}

			// Find the editor target for this project
			if(NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
			{
				List<PerforceFileRecord> Files;
				if(PerforceClient.FindFiles(PerforceUtils.GetClientOrDepotDirectoryName(NewSelectedClientFileName) + "/Source/*Editor.Target.cs", out Files, Log) && Files.Count >= 1)
				{
					PerforceFileRecord File = Files.FirstOrDefault(x => x.Action == null || !x.Action.Contains("delete"));
					if(File == null)
					{
						Log.WriteLine("Couldn't find any non-deleted editor targets for this project.");
					}
					else
					{
						string DepotPath = File.DepotPath;
						NewProjectEditorTarget = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DepotPath.Substring(DepotPath.LastIndexOf('/') + 1)));
						Log.WriteLine("Using {0} as editor target name (from {1})", NewProjectEditorTarget, Files[0]);
					}
				}
				else
				{
					Log.WriteLine("Couldn't find any editor targets for this project.");
				}
			}

			// Get a unique name for the project that's selected. For regular branches, this can be the depot path. For streams, we want to include the stream name to encode imports.
			if(PerforceClient.GetActiveStream(out StreamName, Log))
			{
				string ExpectedPrefix = String.Format("//{0}/", PerforceClient.ClientName);
				if(!NewSelectedClientFileName.StartsWith(ExpectedPrefix, StringComparison.InvariantCultureIgnoreCase))
				{
					ErrorMessage = String.Format("Unexpected client path; expected '{0}' to begin with '{1}'", NewSelectedClientFileName, ExpectedPrefix);
					return false;
				}
				string StreamPrefix;
				if(!TryGetStreamPrefix(PerforceClient, StreamName, Log, out StreamPrefix))
				{
					ErrorMessage = String.Format("Failed to get stream info for {0}", StreamName);
					return false;
				}
				NewSelectedProjectIdentifier = String.Format("{0}/{1}", StreamPrefix, NewSelectedClientFileName.Substring(ExpectedPrefix.Length));
			}
			else
			{
				if(!PerforceClient.ConvertToDepotPath(NewSelectedClientFileName, out NewSelectedProjectIdentifier, Log))
				{
					ErrorMessage = String.Format("Couldn't get depot path for {0}", NewSelectedFileName);
					return false;
				}
			}

			// Read the project logo
			if(NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
			{
				string LogoFileName = Path.Combine(Path.GetDirectoryName(NewSelectedFileName), "Build", "UnrealGameSync.png");
				if(File.Exists(LogoFileName))
				{
					try
					{
						// Duplicate the image, otherwise we'll leave the file locked
						using(Image Image = Image.FromFile(LogoFileName))
						{
							ProjectLogo = new Bitmap(Image);
						}
					}
					catch
					{
						ProjectLogo = null;
					}
				}
			}

			// Succeeed!
			ErrorMessage = null;
			return true;
		}