using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using System.Diagnostics; using System.Threading; using Perforce.P4; namespace Perforce.sln.bld.gui { public partial class FormMain : Form { int lastChange = 0; internal Repository rep = null; private void checkConnect() { if (rep != null) { //release any old connection rep.Dispose(); } String conStr = mServerConnection.Text; String user = mUserText.Text; String password = mPaswordTxt.Text; try { Server server = new Server(new ServerAddress(conStr)); rep = new Repository(server); rep.Connection.UserName = user; Options options = new Options(); options["Password"] = password; rep.Connection.Client = new Client(); rep.Connection.Connect(options); } catch (Exception ex) { rep = null; MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } public FormMain() { InitializeComponent(); } private void mShowPasswordChk_CheckedChanged(object sender, EventArgs e) { mPaswordTxt.UseSystemPasswordChar = !mShowPasswordChk.Checked; } Thread MonitorChangesThread = null; private void mBrowseDepotBtn_Click(object sender, EventArgs e) { checkConnect(); DepotPathDlg dlg = new DepotPathDlg(rep); if (dlg.ShowDialog() == DialogResult.OK) { mSolutionPath.Text = dlg.SelectedFile; int lastChange = GetLastChange(); mLastChangeLbl.Text = lastChange.ToString(); changeAtLastBuild = lastChange; // start the monitor MonitorChangesThread = new Thread(new ThreadStart(MonitorThreadProc)); MonitorChangesThread.IsBackground = true; MonitorChangesThread.Start(); } } private void mSelectBuildDir_Click(object sender, EventArgs e) { folderBrowseDlg.SelectedPath = mBuildFolderTxt.Text; if (folderBrowseDlg.ShowDialog() != DialogResult.Cancel) { mBuildFolderTxt.Text = folderBrowseDlg.SelectedPath; } } private void OnInfoResults(int level, String data) { String spaces = String.Empty; for (int idx = 0; idx < level; idx++) { spaces += "."; } AsynchAddLineToLog(String.Format("{0}{1}", spaces, data)); } StreamWriter log; int changeAtLastBuild = 0; private void mBuildNowBtn_Click(object sender, EventArgs e) { RunBuild(true); } private void RunBuild(bool async) { DateTime buildTime = DateTime.Now; String buildId = buildTime.ToString("MMddyyHHmmss"); String buildFolder = buildTime.ToString("MM-dd-yy_HHmmss"); String buildPath = Path.Combine(mBuildFolderTxt.Text, buildFolder); int idx = 0; while ((Directory.Exists(buildPath)) && (idx < 26)) { buildPath = Path.Combine(mBuildFolderTxt.Text, buildTime.ToString("MM-dd-yy HHmmss") + ((char)((int)'a' + idx))); } if (idx >= 26) return; string logFile = Path.Combine(buildPath, "BuildLog.txt"); Directory.CreateDirectory(buildPath); String ConStr = mServerConnection.Text; String User = mUserText.Text; String Password = mPaswordTxt.Text; String Target = mSolutionPath.Text; Server pServer = new Server(new ServerAddress(ConStr)); rep = new Repository(pServer); Connection con = rep.Connection; con.Connect(null); log = new StreamWriter(logFile); Client buildClient = new Client(); buildClient.Name = "p4apinet_solution_builder_sample_application_client"; buildClient.OwnerName = con.UserName; buildClient.ViewMap = new ViewMap(); buildClient.Root = buildPath; buildClient.Options = ClientOption.AllWrite; buildClient.LineEnd = LineEnd.Local; buildClient.SubmitOptions = new ClientSubmitOptions(false, SubmitType.SubmitUnchanged); string depotPath = mSolutionPath.Text; IList fmd = null; try { fmd = rep.GetFileMetaData(null, FileSpec.DepotSpec(depotPath)); } catch{} if (( fmd == null) || (fmd.Count !=1)) { string message = string.Format("The solution file \"{0}\" does not exist in the depot.", depotPath); MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (mSolutionPath.Text.EndsWith(".sln")) { depotPath = mSolutionPath.Text.Substring(0, mSolutionPath.Text.LastIndexOf('/')); } string depotFolder = depotPath.Substring(depotPath.LastIndexOf('/') + 1); depotPath += "/..."; String clientPath = String.Format("//{0}/{1}/...", buildClient.Name, depotFolder); MapEntry entry = new MapEntry(MapType.Include, new DepotPath(depotPath), new ClientPath(clientPath)); buildClient.ViewMap.Add(entry); rep.CreateClient(buildClient); con.Client = rep.GetClient(buildClient.Name); string localPath = clientPath; localPath = localPath.TrimStart('/'); localPath = localPath.TrimEnd('.'); localPath = localPath.Substring(localPath.IndexOf('/') + 1); localPath = localPath.Replace('/', '\\'); string solutionName = Target.Substring(depotPath.LastIndexOf('/')); solutionName = solutionName.TrimStart('/'); localPath = Path.Combine(buildPath, localPath, solutionName); int lastChange = GetLastChange(); AsynchSetLastChange(lastChange); IList changes = GetChangesAfter(changeAtLastBuild, lastChange); changeAtLastBuild = lastChange; if (changes != null) { for (idx = 0; idx < changes.Count; idx++) { AsynchAddLineToLog(changes[idx].ToString(true)); } } if (async) { Thread buildThread = new Thread(new ParameterizedThreadStart(RunBuildProc)); buildThread.IsBackground = true; buildThread.Start(localPath); } else { RunBuildProc(localPath); } con.Disconnect(null); } private void mSelectMSBuildLoactionBtn_Click(object sender, EventArgs e) { fileBrowseDlg.FileName = mMsBuildPathTxt.Text; if (fileBrowseDlg.ShowDialog() != DialogResult.Cancel) { mMsBuildPathTxt.Text = fileBrowseDlg.FileName; } } private int GetLastChange() { string depotPath = mSolutionPath.Text; if (mSolutionPath.Text.EndsWith(".sln")) { depotPath = mSolutionPath.Text.Substring(0, mSolutionPath.Text.LastIndexOf('/')); } String ConStr = mServerConnection.Text; String User = mUserText.Text; String Password = mPaswordTxt.Text; String Target = mSolutionPath.Text; Server pServer = new Server(new ServerAddress(ConStr)); rep = new Repository(pServer); Connection con = rep.Connection; con.Connect(null); Options opts = new Options(); opts["-m"]="1"; IList changes = rep.GetChangelists(opts, null); if (changes != null) return (int) changes[0].Id; return -1; } private IList GetChangesAfter(int previous, int current) { if (current <= previous) return null; string depotPath = mSolutionPath.Text; if (mSolutionPath.Text.EndsWith(".sln")) { depotPath = mSolutionPath.Text.Substring(0, mSolutionPath.Text.LastIndexOf('/')); } FileSpec fs = new DepotPath(depotPath); Options opts = new Options(); opts["-m"] = (current - previous).ToString(); IList changes = rep.GetChangelists(opts, fs); return changes; } Process buildProc = null; bool buildFailed = false; private void RunBuildProc(object oSolutionFilePath) { checkConnect(); Connection con = rep.Connection; Client buildClient = new Client(); buildClient.Name = "p4apinet_solution_builder_sample_application_client"; con.Client = rep.GetClient(buildClient.Name); Options sFlags = new Options((SyncFilesCmdFlags.Force|SyncFilesCmdFlags.PopulateClient), -1); IList rFiles = con.Client.SyncFiles(sFlags, null); Options opts = new Options(); opts["-f"] = null; rep.DeleteClient(buildClient,opts); buildFailed = false; String SolutionFilePath = (String)oSolutionFilePath; String MSBuildPath = (String)mMsBuildPathTxt.Text; buildProc = new Process(); string quotedPath = string.Format("\"{0}\"", SolutionFilePath); ProcessStartInfo si = new ProcessStartInfo(MSBuildPath, quotedPath); si.UseShellExecute = false; si.RedirectStandardOutput = true; si.RedirectStandardError = true; si.WorkingDirectory = Path.GetDirectoryName(SolutionFilePath); si.CreateNoWindow = false; buildProc.StartInfo = si; Thread stdoutThread = new Thread(new ThreadStart(ReadStandardOutThreadProc)); stdoutThread.IsBackground = true; Thread stderrThread = new Thread(new ThreadStart(ReadStandardErrorThreadProc)); stderrThread.IsBackground = true; buildProc.Start(); stdoutThread.Start(); stderrThread.Start(); buildProc.WaitForExit(); if (stdoutThread.IsAlive) stdoutThread.Abort(); if (stderrThread.IsAlive) stderrThread.Abort(); buildProc = null; log.Dispose(); log = null; AsynchSetLastChange(buildFailed); return; } private delegate void AddLineDelegate(String Line); public void AddLineToLog(string line) { if (mLogText.TextLength > 0) mLogText.Text += "\r\n"; mLogText.Text += line; mLogText.Select(mLogText.Text.Length, 0); mLogText.ScrollToCaret(); } private void AsynchAddLineToLog( String line) { if (AddLine == null) AddLine = new AddLineDelegate(AddLineToLog); if (log != null) { log.WriteLine(line); } mLogText.BeginInvoke(AddLine, line); } AddLineDelegate AddLine = null; private delegate void SetLastChangeDelegate(int Line); public void SetLastChange(int change ) { mLastChangeLbl.Text = change.ToString(); } SetLastChangeDelegate SetLast = null; private void AsynchSetLastChange( int change ) { if (SetLast == null) SetLast = new SetLastChangeDelegate(SetLastChange); mLastChangeLbl.BeginInvoke(SetLast, change); } private delegate void ShowBuildFailedDelegate(bool show); public void ShowBuildFailed(bool show) { mBuildFailedLbl.Visible = show; } ShowBuildFailedDelegate ShowFail = null; private void AsynchSetLastChange( bool show ) { if (ShowFail == null) ShowFail = new ShowBuildFailedDelegate(ShowBuildFailed); mLastChangeLbl.BeginInvoke(ShowFail, show); } private void ReadStandardOutThreadProc() //Process p, TextBox t) { try { String line; while ((line = buildProc.StandardOutput.ReadLine()) != null) { AsynchAddLineToLog(line); if (line.Contains("FAILED.")) buildFailed = true; } return; } catch (ThreadAbortException) { return; } } private void ReadStandardErrorThreadProc() //Process p, TextBox t) { try { String line; while ((line = buildProc.StandardError.ReadLine()) != null) { AsynchAddLineToLog(line); } return; } catch (ThreadAbortException) { return; } } int mBuildInterval = 2; private void MonitorThreadProc() { try { while (true) { int last = GetLastChange(); mLogText.BeginInvoke(new AddLineDelegate(AddLineToLog), String.Format("[{0} {1}] Checking for changes....", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString())); if (last > changeAtLastBuild) { mLogText.BeginInvoke(new AddLineDelegate(AddLineToLog), "Changes found"); RunBuild(false); } AsynchAddLineToLog( String.Format("Next Check at {0}", DateTime.Now.AddMinutes((double)mBuildInterval).ToShortTimeString())); Thread.Sleep(TimeSpan.FromMinutes(mBuildInterval)); } } catch (ThreadAbortException) { } } private void mBuildIntervalCmb_SelectedIndexChanged(object sender, EventArgs e) { int.TryParse((String)mBuildIntervalCmb.SelectedItem, out mBuildInterval); } private void FormMain_Load(object sender, EventArgs e) { ReadSettings(); if (String.IsNullOrEmpty(mSolutionPath.Text)) return; lastChange = GetLastChange(); mLastChangeLbl.Text = lastChange.ToString(); changeAtLastBuild = lastChange; // start the monitor MonitorChangesThread = new Thread(new ThreadStart(MonitorThreadProc)); MonitorChangesThread.IsBackground = true; MonitorChangesThread.Start(); } private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { SaveSettings(); } private void SaveSettings() { using (StreamWriter sw = new StreamWriter("buildProc.config")) { //Credentials if (mShowPasswordChk.Checked) sw.Write('1'); else sw.Write('0'); sw.WriteLine(mPaswordTxt.Text); sw.WriteLine(mUserText.Text); sw.WriteLine(mServerConnection.Text); sw.WriteLine(mBuildFolderTxt.Text); sw.WriteLine(mMsBuildPathTxt.Text); sw.WriteLine(mBuildIntervalCmb.SelectedIndex.ToString()); sw.WriteLine(mSolutionPath.Text); } } private void ReadSettings() { if (!System.IO.File.Exists("buildProc.config")) return; using (StreamReader sr = new StreamReader("buildProc.config")) { //Credentials String line = sr.ReadLine(); mShowPasswordChk.Checked = (line[0] == '1'); mPaswordTxt.Text = line.Substring(1); mUserText.Text = sr.ReadLine(); mServerConnection.Text = sr.ReadLine(); mBuildFolderTxt.Text = sr.ReadLine(); mMsBuildPathTxt.Text = sr.ReadLine(); line = sr.ReadLine(); int idx = 1; int.TryParse(line, out idx); mBuildIntervalCmb.SelectedIndex = idx; mSolutionPath.Text = sr.ReadLine(); } } private void mUserText_TextChanged(object sender, EventArgs e) { } private void mSolutionPath_TextChanged(object sender, EventArgs e) { string path = mSolutionPath.Text; mBuildNowBtn.Enabled = !string.IsNullOrEmpty(path); } } }